<!-- 博客 - 页面布局 > 上中下 -->



<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=dark>
<!-- 博客 - 导航栏上方 -->


<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/blog/img/fluid.png">
  <link rel="icon" href="/blog/img/icon/dw.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="Memory">
  <meta name="keywords" content="">

  
    <meta name="description" content="🚄 Java狂热者的神秘宝藏之旅，一场酣畅淋漓的Java知识盛宴，让你的程序更强大、更快速">
<meta property="og:type" content="article">
<meta property="og:title" content="Java终极冒险：从基础到实战，解锁Java的神秘密码">
<meta property="og:url" content="https://test.atomgit.net/blog/2023/10/24/Java%E7%BB%88%E6%9E%81%E5%86%92%E9%99%A9%EF%BC%9A%E4%BB%8E%E5%9F%BA%E7%A1%80%E5%88%B0%E5%AE%9E%E6%88%98%EF%BC%8C%E8%A7%A3%E9%94%81Java%E7%9A%84%E7%A5%9E%E7%A7%98%E5%AF%86%E7%A0%81/index.html">
<meta property="og:site_name" content="Memory&#39;s blog">
<meta property="og:description" content="🚄 Java狂热者的神秘宝藏之旅，一场酣畅淋漓的Java知识盛宴，让你的程序更强大、更快速">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://test.atomgit.net/blog/img/artical/afternoon4.jpg">
<meta property="article:published_time" content="2023-10-24T07:09:16.000Z">
<meta property="article:modified_time" content="2025-07-31T05:16:59.000Z">
<meta property="article:author" content="Memory">
<meta property="article:tag" content="线程池">
<meta property="article:tag" content="并发编程">
<meta property="article:tag" content="JUC">
<meta property="article:tag" content="经验">
<meta property="article:tag" content="Java">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://test.atomgit.net/blog/img/artical/afternoon4.jpg">
  
  
  
  <title>Java终极冒险：从基础到实战，解锁Java的神秘密码 - Memory&#39;s blog</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/blog/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/blog/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/blog/css/highlight-dark.css" />
  



  
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css">



  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"test.atomgit.net","root":"/blog/","version":"1.9.4","typing":{"enable":true,"typeSpeed":100,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"right","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":false,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":true,"follow_dnt":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"appId":"T1hUHH9Ks1ggG9DBE3HgUPpV-gzGzoHsz","appKey":"XFkgzzvzNo3J3uNCbBiSOFKc","server_url":"https://t1huhh9k.lc-cn-n1-shared.com","path":"window.location.pathname","ignore_local":false}},"search_path":"/blog/local-search.xml"};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/blog/js/utils.js" ></script>
  <script  src="/blog/js/color-schema.js" ></script>
  

  

  

  

  

  

  

  



  
<meta name="generator" content="Hexo 5.4.2"></head>

<body>
  

  <header>
    <!-- 博客 - 导航栏上方 -->


<div class="header-inner" style="height: 100vh">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/blog/">
      <strong>Memory&#39;s blog</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/">
                <i class="iconfont icon-home-fill"></i>
                <span>首页</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/archives/">
                <i class="iconfont icon-archive-fill"></i>
                <span>归档</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/categories/">
                <i class="iconfont icon-category-fill"></i>
                <span>分类</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/tags/">
                <i class="iconfont icon-tags-fill"></i>
                <span>标签</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/about/">
                <i class="iconfont icon-user-fill"></i>
                <span>关于</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/links/">
                <i class="iconfont icon-link-fill"></i>
                <span>友链</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/playlist/">
                <i class="iconfont icon-music"></i>
                <span>音乐</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/Memos/">
                <i class="iconfont icon-comment "></i>
                <span>说说</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/App/">
                <i class="iconfont icon-mobile"></i>
                <span>咫尺</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item dropdown">
              <a class="nav-link dropdown-toggle" target="_self" href="javascript:;" role="button"
                 data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                <i class="iconfont icon-books"></i>
                <span>青简</span>
              </a>
              <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                
                  
                  
                  
                  <a class="dropdown-item" target="_blank" rel="noopener" href="http://47.93.135.62:5230/explore">
                    <i class="iconfont icon-images"></i>
                    <span>碎念微光</span>
                  </a>
                
                  
                  
                  
                  <a class="dropdown-item" href="/blog/New/">
                    <i class="iconfont icon-bug"></i>
                    <span>匠册详析</span>
                  </a>
                
                  
                  
                  
                  <a class="dropdown-item" href="/blog/New/">
                    <i class="iconfont icon-xbox-fill"></i>
                    <span>窗景新编</span>
                  </a>
                
                  
                  
                  
                  <a class="dropdown-item" href="/blog/New/">
                    <i class="iconfont icon-steam"></i>
                    <span>戏码幕后</span>
                  </a>
                
                  
                  
                  
                  <a class="dropdown-item" href="/blog/New/">
                    <i class="iconfont icon-map"></i>
                    <span>他山随拾</span>
                  </a>
                
              </div>
            </li>
          
        
        
            <li class="nav-item" id="search-btn">
                <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalNotice" aria-label="Search">
                    <i class="iconfont icon-book"></i>
                    <span>公告</span>
                </a>
            </li>
            
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              <i class="iconfont icon-search"></i>
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">
              <i class="iconfont icon-dark" id="color-toggle-icon"></i>
            </a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>
 
<div id="banner" class="banner" parallax=true
     style="background: url('/blog/img/artical/afternoon4.jpg') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">

    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">

        <div class="h2">
          
            <span id="subtitle" data-typed-text="Java终极冒险：从基础到实战，解锁Java的神秘密码"></span>
          
        </div>


        
          <!-- 文章页面 - 顶部信息 -->

<div class="mt-3">
  
  <span class="post-meta mr-2">
    <i class="iconfont icon-author" aria-hidden="true"></i>
    Memory
  </span>
   
  <span class="post-meta">
    <i class="iconfont icon-date-fill" aria-hidden="true"></i>
    <time datetime="2023-10-24 15:09" pubdate>
      2023年10月24日 下午
    </time>
  </span>
  
</div>

<div class="mt-1">
  
  <span class="post-meta mr-2">
    <i class="iconfont icon-chart"></i>
     59k 字 
  </span>
   
  <span class="post-meta mr-2">
    <i class="iconfont icon-clock-fill"></i>
       149 分钟 
  </span>
     
  <span
    id="leancloud-page-views-container"
    class="post-meta"
    style="display: none"
  >
    <i class="iconfont icon-eye" aria-hidden="true"></i>
    <span id="leancloud-page-views"></span> 次
  </span>

    
</div>


        

        <!-- 添加网站运行时间 -->
        <div class="footer-content" style="background-color: transparent;">
          <div>
            <span id="timeDate"></span>
            <span id="times"></span>
            <script>
            var now = new Date();
            function createtime(){
                var grt= new Date("03/07/2023 00:00:00");//此处修改你的建站时间或者网站上线时间
                now.setTime(now.getTime()+250);
                days = (now - grt ) / 1000 / 60 / 60 / 24;
                dnum = Math.floor(days);
                hours = (now - grt ) / 1000 / 60 / 60 - (24 * dnum);
                hnum = Math.floor(hours);
                if(String(hnum).length ==1 ){
                    hnum = "0" + hnum;
                }
                minutes = (now - grt ) / 1000 /60 - (24 * 60 * dnum) - (60 * hnum);
                mnum = Math.floor(minutes);
                if(String(mnum).length ==1 ){
                          mnum = "0" + mnum;
                }
                seconds = (now - grt ) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum);
                snum = Math.round(seconds);
                if(String(snum).length ==1 ){
                          snum = "0" + snum;
                }
                document.getElementById("timeDate").innerHTML = "🚀 &nbsp"+dnum+"&nbsp天";  //此次自定义显示内容
                document.getElementById("times").innerHTML = hnum + "&nbsp小时&nbsp" + mnum + "&nbsp分&nbsp" + snum + "&nbsp秒";
            }  

            if (!window.location.href.includes('/20')) {  
            setInterval("createtime()", 250);  // 只有当URL不包含'specific-page'时，才调用createtime  
            }//此次自定义显示内容
            </script>
          </div>
        </div>

      </div>
      
        <div class="scroll-down-bar">
          <i class="iconfont icon-arrowdown"></i>
        </div>
      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      <!-- 文章页  -->


<div class="container-fluid nopadding-x">

  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar category-bar" style="margin-right: -1rem">
    <!-- 文章页面 - 左边栏所属分类 -->
  

<!-- 文章页 - 文章分类 -->





<div class="category-list">
  
  
    
    
    
    <div class="category row nomargin-x">
      <a class="category-item 
          list-group-item category-item-action col-10 col-md-11 col-xm-11" title="代码锋芒"
        id="heading-843238cb7c4b39b52e95311733ff6da6" role="tab" data-toggle="collapse" href="#collapse-843238cb7c4b39b52e95311733ff6da6"
        aria-expanded="true"
      >
        代码锋芒
        <span class="list-group-count">(31)</span>
        <i class="iconfont icon-arrowright"></i>
      </a>
      
      <div class="category-collapse collapse show" id="collapse-843238cb7c4b39b52e95311733ff6da6"
           role="tabpanel" aria-labelledby="heading-843238cb7c4b39b52e95311733ff6da6">
        
        
          
          
  <div class="category-post-list">
    
    
      
      
        <a href="/blog/2023/03/19/MySQL%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE/" title="MySQL安装配置"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">MySQL安装配置</span>
        </a>
      
    
      
      
        <a href="/blog/2023/03/22/MySQL%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/" title="MySQL从入门到入土"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">MySQL从入门到入土</span>
        </a>
      
    
      
      
        <a href="/blog/2023/04/09/Java%E6%B3%9B%E5%9E%8B%E7%9A%84%E7%90%86%E8%A7%A3/" title="Java泛型：赋予灵活性的利器"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">Java泛型：赋予灵活性的利器</span>
        </a>
      
    
      
      
        <a href="/blog/2023/05/21/%E6%8E%A2%E7%B4%A2%20Python%20%E7%88%AC%E8%99%AB%E7%9A%84%E6%97%A0%E9%99%90%E5%8F%AF%E8%83%BD%E6%80%A7%EF%BC%9A%E5%9F%BA%E7%A1%80%E4%B8%8E%E5%BA%94%E7%94%A8/" title="探索Python爬虫的无限可能性：基础与应用"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">探索Python爬虫的无限可能性：基础与应用</span>
        </a>
      
    
      
      
        <a href="/blog/2023/06/03/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%9A%84%20JavaWeb%20%E4%B9%8B%E6%97%85/" title="从零开始的 JavaWeb 之旅"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">从零开始的 JavaWeb 之旅</span>
        </a>
      
    
      
      
        <a href="/blog/2023/06/10/%E6%95%B0%E6%8D%AE%E5%BA%93-%E5%AE%8C%E6%95%B4%E6%80%A7%E7%BA%A6%E6%9D%9F/" title="数据库-完整性约束"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">数据库-完整性约束</span>
        </a>
      
    
      
      
        <a href="/blog/2023/06/24/%E4%BD%BF%E7%94%A8%20JSP%20%E6%9E%84%E5%BB%BA%20JavaWeb%20%E9%A1%B9%E7%9B%AE-%E4%BB%A5%E6%95%B0%E6%8D%AE%E5%BA%93%E5%A4%A7%E4%BD%9C%E4%B8%9A%E4%B8%BA%E4%BE%8B/" title="使用JSP构建JavaWeb项目--以数据库大作业为例"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">使用JSP构建JavaWeb项目--以数据库大作业为例</span>
        </a>
      
    
      
      
        <a href="/blog/2023/07/06/%E6%89%93%E9%80%A0%E6%B5%81%E7%95%85%E7%BC%96%E7%A0%81%E4%B9%8B%E6%97%85%EF%BC%9AIntelliJ%20IDEA%20%E7%9A%84%E9%AB%98%E6%95%88%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7%E4%B8%8E%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B%E4%BC%98%E5%8C%96/" title="打造流畅编码之旅：IntelliJ IDEA 的高效使用技巧与工作流程优化"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">打造流畅编码之旅：IntelliJ IDEA 的高效使用技巧与工作流程优化</span>
        </a>
      
    
      
      
        <a href="/blog/2023/07/06/%E5%87%A4%E5%87%B0%E6%B6%85%E6%A7%83%EF%BC%9ASpring%20Boot%20%E5%BC%80%E5%8F%91%E4%B9%8B%E8%B7%AF%E4%B8%8A%E7%9A%84%E5%9D%8E%E5%9D%B7%E4%B8%8E%E6%88%90%E9%95%BF/" title="凤凰涅槃：Spring Boot 开发之路上的坎坷与成长"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">凤凰涅槃：Spring Boot 开发之路上的坎坷与成长</span>
        </a>
      
    
      
      
        <a href="/blog/2023/07/11/%E4%BB%8EIP%E5%88%B0Ping%E5%91%BD%E4%BB%A4%EF%BC%9A%E4%BA%86%E8%A7%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E8%BF%9E%E6%8E%A5%E7%9A%84%E5%9F%BA%E7%A1%80/" title="从IP到Ping命令：了解计算机网络连接的基础"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">从IP到Ping命令：了解计算机网络连接的基础</span>
        </a>
      
    
      
      
        <a href="/blog/categories/%E4%BB%A3%E7%A0%81%E9%94%8B%E8%8A%92/" class="list-group-item list-group-item-action">
          <span class="category-post">More...</span>
        </a>
        
  </div>

          
  
    
    
    
    <div class="category-sub row nomargin-x">
      <a class="category-subitem collapsed
          list-group-item category-item-action col-10 col-md-11 col-xm-11" title="职路星火"
        id="heading-59c550454d1ecf67a2ead26a1c46d14b" role="tab" data-toggle="collapse" href="#collapse-59c550454d1ecf67a2ead26a1c46d14b"
        aria-expanded="false"
      >
        职路星火
        <span class="list-group-count">(2)</span>
        <i class="iconfont icon-arrowright"></i>
      </a>
      
      <div class="category-collapse collapse " id="collapse-59c550454d1ecf67a2ead26a1c46d14b"
           role="tabpanel" aria-labelledby="heading-59c550454d1ecf67a2ead26a1c46d14b">
        
        
          
  <div class="category-post-list">
    
    
      
      
        <a href="/blog/2024/03/07/%E7%A0%BA%E5%89%91%E5%87%BA%E9%9E%98%EF%BC%9A%E6%88%91%E7%9A%84%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%B1%82%E8%81%8C%E4%B9%8B%E6%97%85/" title="砺剑出鞘：我的软件工程师求职之旅"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">砺剑出鞘：我的软件工程师求职之旅</span>
        </a>
      
    
      
      
        <a href="/blog/2024/04/16/%E6%8B%A8%E4%BA%91%E8%A7%81%E6%97%A5%EF%BC%9A%E8%81%8C%E5%9C%BA%E7%94%9F%E6%B4%BB%E7%9A%84%E7%82%B9%E6%BB%B4%E8%AE%B0%E5%BD%95/" title="拨云见日：职场生活的点滴记录"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">拨云见日：职场生活的点滴记录</span>
        </a>
      
    
  </div>

        
      </div>
    </div>
  
    
    
    
    <div class="category-sub row nomargin-x">
      <a class="category-subitem collapsed
          list-group-item category-item-action col-10 col-md-11 col-xm-11" title="部署之道"
        id="heading-36acf247b1a64f4bbe423776a392cfde" role="tab" data-toggle="collapse" href="#collapse-36acf247b1a64f4bbe423776a392cfde"
        aria-expanded="false"
      >
        部署之道
        <span class="list-group-count">(5)</span>
        <i class="iconfont icon-arrowright"></i>
      </a>
      
      <div class="category-collapse collapse " id="collapse-36acf247b1a64f4bbe423776a392cfde"
           role="tabpanel" aria-labelledby="heading-36acf247b1a64f4bbe423776a392cfde">
        
        
          
          
  <div class="category-post-list">
    
    
      
      
        <a href="/blog/2023/06/22/Nginx%20%E5%85%A5%E9%97%A8%E6%8C%87%E5%8D%97%EF%BC%9A%E7%90%86%E8%A7%A3%E3%80%81%E9%85%8D%E7%BD%AE%E5%92%8C%E4%BC%98%E5%8C%96/" title="Nginx 入门指南：理解、配置和优化"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">Nginx 入门指南：理解、配置和优化</span>
        </a>
      
    
      
      
        <a href="/blog/2023/07/11/Docker%20%E5%85%A5%E9%97%A8%E4%B8%8E%E5%AE%9E%E8%B7%B5%EF%BC%9A%E5%BF%AB%E9%80%9F%E6%89%93%E9%80%A0%E9%AB%98%E6%95%88%E7%BD%91%E7%AB%99%E9%83%A8%E7%BD%B2%E7%8E%AF%E5%A2%83/" title="Docker入门与实践：快速打造高效网站部署环境"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">Docker入门与实践：快速打造高效网站部署环境</span>
        </a>
      
    
      
      
        <a href="/blog/2023/08/17/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20API%20%E7%BD%91%E5%85%B3%EF%BC%9A%E5%8E%9F%E7%90%86%E3%80%81%E5%8A%9F%E8%83%BD%E5%92%8C%E7%94%A8%E9%80%94/" title="深入理解 API 网关：原理、功能和用途"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">深入理解 API 网关：原理、功能和用途</span>
        </a>
      
    
  </div>

          
  
    
    
    
    <div class="category-sub row nomargin-x">
      <a class="category-subitem collapsed
          list-group-item category-item-action col-10 col-md-11 col-xm-11" title="职路星火"
        id="heading-59c550454d1ecf67a2ead26a1c46d14b" role="tab" data-toggle="collapse" href="#collapse-59c550454d1ecf67a2ead26a1c46d14b"
        aria-expanded="false"
      >
        职路星火
        <span class="list-group-count">(2)</span>
        <i class="iconfont icon-arrowright"></i>
      </a>
      
      <div class="category-collapse collapse " id="collapse-59c550454d1ecf67a2ead26a1c46d14b"
           role="tabpanel" aria-labelledby="heading-59c550454d1ecf67a2ead26a1c46d14b">
        
        
          
  <div class="category-post-list">
    
    
      
      
        <a href="/blog/2024/12/16/%E6%BC%AB%E6%BC%AB%E5%BE%81%E9%80%94%EF%BC%9A%E7%AD%91%E6%A2%A6%E4%BA%8E%E8%81%8C%E5%9C%BA%E9%A3%8E%E9%9B%A8%EF%BC%8C%E5%90%9F%E5%92%8F%E4%BA%8E%E6%97%A5%E5%B8%B8%E8%AF%97%E6%84%8F/" title="漫漫征途：筑梦于职场风雨，吟咏于日常诗意"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">漫漫征途：筑梦于职场风雨，吟咏于日常诗意</span>
        </a>
      
    
      
      
        <a href="/blog/2025/10/23/%E4%BF%9D%E9%A9%BE%E6%8A%A4%E8%88%AA%E2%80%94%E2%80%94%E5%90%8E%E5%8F%B0%E8%BF%90%E7%BB%B4%E8%A7%86%E8%A7%92%E4%B8%8B%E7%9A%84%E9%AB%98%E9%80%9F%E5%85%AC%E8%B7%AF%E2%80%9C%E7%94%9F%E5%91%BD%E7%BA%BF%E2%80%9D/" title="保驾护航——后台运维视角下的高速公路“生命线”"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">保驾护航——后台运维视角下的高速公路“生命线”</span>
        </a>
      
    
  </div>

        
      </div>
    </div>
  
        
      </div>
    </div>
  
    
    
    
    <div class="category-sub row nomargin-x">
      <a class="category-subitem collapsed
          list-group-item category-item-action col-10 col-md-11 col-xm-11" title="项目志录"
        id="heading-005e56101bc2a873707cb4461a70ded8" role="tab" data-toggle="collapse" href="#collapse-005e56101bc2a873707cb4461a70ded8"
        aria-expanded="false"
      >
        项目志录
        <span class="list-group-count">(1)</span>
        <i class="iconfont icon-arrowright"></i>
      </a>
      
      <div class="category-collapse collapse " id="collapse-005e56101bc2a873707cb4461a70ded8"
           role="tabpanel" aria-labelledby="heading-005e56101bc2a873707cb4461a70ded8">
        
        
          
  <div class="category-post-list">
    
    
      
      
        <a href="/blog/2023/07/30/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%8F%91%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB%EF%BC%9A%E8%B8%A9%E5%9D%91%E6%8C%87%E5%8D%97%E4%B8%8E%E6%8A%80%E5%B7%A7%E6%80%BB%E7%BB%93/" title="小程序开发经验分享：踩坑指南与技巧总结"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">小程序开发经验分享：踩坑指南与技巧总结</span>
        </a>
      
    
  </div>

        
      </div>
    </div>
  
        
      </div>
    </div>
  
</div>

  </aside>


    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">Java终极冒险：从基础到实战，解锁Java的神秘密码</h1>
            
              <p class="note note-success">
                
                  
                    本文最后更新于：4 个月前
                  
                
              </p>
            
            
              <div class="markdown-body">
                
                <h1 id="破冰"><a href="#破冰" class="headerlink" title="破冰"></a>破冰</h1><p>🥇 推荐阅读：</p>
<p>🍖 反射：</p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7288988872829190204">java 中的反射原理，为什么要使用反射以及反射使用场景（面试常问） - 掘金 (juejin.cn)</a></p>
<p><a target="_blank" rel="noopener" href="https://javaguide.cn/java/basis/reflection.html">Java 反射机制详解 | JavaGuide(Java 面试 + 学习指南)</a></p>
<p>☕ 代理：</p>
<p><a target="_blank" rel="noopener" href="https://javaguide.cn/java/basis/proxy.html">Java 代理模式详解 | JavaGuide(Java 面试 + 学习指南)</a></p>
<p>🥣 注解：</p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7213910450994905147">详解 JAVA 注解机制 - 掘金 (juejin.cn)</a></p>
<p>🌮 语法糖：</p>
<p><a target="_blank" rel="noopener" href="https://javaguide.cn/java/basis/syntactic-sugar.html">Java 语法糖详解 | JavaGuide(Java 面试 + 学习指南)</a></p>
<p>🍛 集合：</p>
<p><a target="_blank" rel="noopener" href="https://zhangfz.blog.csdn.net/article/details/132474104?spm=1001.2014.3001.5502">【Java 从 0 到 1 学习】11 Java 集合框架-CSDN 博客</a></p>
<p>巩固基础：</p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/column/7238814783597363256">混凝土巩基 - 竹子爱熊猫的专栏 - 掘金</a></p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7373136303179743243#heading-47">如何干掉你代码里的if，让请求参数校验变的更加优雅？你是否还在写if来完成参数校验？如何有效减少代码中的if数量，并使得 - 掘金</a></p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7241395267797942329#heading-13">大家都说Java有三种创建线程的方式！并发编程中的惊天骗局！Java中有几种创建线程的方式？这是一道Java Plus版 - 掘金</a></p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7238823745828405308">四十五图，一万五千字！一文让你走出迷雾玩转Maven！Maven是大家的老熟客，几乎每天都会跟他打交道，不过许多人对它似 - 掘金</a></p>
<h1 id="思维碰撞"><a href="#思维碰撞" class="headerlink" title="思维碰撞"></a>思维碰撞</h1><h2 id="序列化"><a href="#序列化" class="headerlink" title="序列化"></a>序列化</h2><blockquote>
<p>2025 年  7 月 31 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7264414580774928443">(十二)探索高性能通信与RPC框架基石：Json、ProtoBuf、Hessian序列化详解如今这个分布式风靡的时代，网 - 掘金</a></p>
<p>上面扯了一堆理论，下面简单实战一下，代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-meta">@Data</span><br><span class="hljs-meta">@AllArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ZhuZi</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Serializable</span> &#123;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">long</span> <span class="hljs-variable">serialVersionUID</span> <span class="hljs-operator">=</span> <span class="hljs-number">1L</span>;<br>    <span class="hljs-comment">// 序号</span><br>    <span class="hljs-keyword">private</span> Integer id;<br>    <span class="hljs-comment">// 名称</span><br>    <span class="hljs-keyword">private</span> String name;<br>    <span class="hljs-comment">// 等级</span><br>    <span class="hljs-keyword">private</span> String grade;<br>&#125;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SerializableDemo</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-comment">// 1. 序列化</span><br>        <span class="hljs-type">ZhuZi</span> <span class="hljs-variable">zhuZi</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ZhuZi</span>(<span class="hljs-number">1</span>, <span class="hljs-string">&quot;黄金竹子&quot;</span>, <span class="hljs-string">&quot;A级&quot;</span>);<br>        <span class="hljs-type">byte</span>[] serializeBytes = serialize(zhuZi);<br>        System.out.println(<span class="hljs-string">&quot;JDK序列化后的字节数组长度：&quot;</span> + serializeBytes.length);<br><br>        <span class="hljs-comment">// 2. 反序列化</span><br>        <span class="hljs-type">ZhuZi</span> <span class="hljs-variable">deserializeZhuZi</span> <span class="hljs-operator">=</span> deserialize(serializeBytes);<br>        System.out.println(deserializeZhuZi.toString());<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 序列化方法</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> zhuZi 需要序列化的对象</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 序列化后生成的字节流</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">byte</span>[] serialize(ZhuZi zhuZi) <span class="hljs-keyword">throws</span> IOException &#123;<br>        <span class="hljs-type">ByteArrayOutputStream</span> <span class="hljs-variable">bos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ByteArrayOutputStream</span>();<br>        <span class="hljs-type">ObjectOutputStream</span> <span class="hljs-variable">oos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ObjectOutputStream</span>(bos);<br>        oos.writeObject(zhuZi);<br>        <span class="hljs-keyword">return</span> bos.toByteArray();<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 反序列化方法</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> bytes 字节序列（字节流）</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 实体类对象</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> ZhuZi <span class="hljs-title function_">deserialize</span><span class="hljs-params">(<span class="hljs-type">byte</span>[] bytes)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-type">ByteArrayInputStream</span> <span class="hljs-variable">bis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ByteArrayInputStream</span>(bytes);<br>        <span class="hljs-type">ObjectInputStream</span> <span class="hljs-variable">ois</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ObjectInputStream</span>(bis);<br>        <span class="hljs-keyword">return</span> (ZhuZi) ois.readObject();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>为了减少代码量，这里用了<code>Lombok</code>的注解，上面这个案例没啥好说的，相信大家曾经都学习过，这里说明几点：</p>
<ul>
<li>①<code>Serializable</code>具备向下传递性，父类实现了该接口，子类默认实现序列化接口；</li>
<li>②<code>Serializable</code>具备引用传递性，两个实现<code>Serializable</code>接口的类产生引用时，序列化时会一起处理；</li>
<li>③序列化前的对象，和反序列化得到的对象，如案例中的<code>zhuZi、deserializeZhuZi</code>，是两个状态完全相同的不同对象，相当于一次深拷贝；</li>
<li>④<code>JDK</code>并不会序列化静态变量，因为序列化只会保存对象的状态，而静态成员属于类的“状态”；</li>
<li>⑤序列化机制会打破单例模式，如果一个单例对象要序列化，一定要手写一次<code>readResolve()</code>方法；</li>
<li>④<code>Serializable</code>默认会把所有字段序列化，网络传输时想要对字段脱敏，可以结合<code>transient</code>关键字使用。</li>
</ul>
<p>重点来看看最后一点，这里提到一个少见的<code>Java</code>原生关键字，我们可以将<code>ZhuZi</code>类的一个属性，加上<code>transient</code>关键字做个实验，如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">java<br><br> 体验AI代码助手<br> 代码解读<br>复制代码<span class="hljs-keyword">private</span> <span class="hljs-keyword">transient</span> String grade;<br></code></pre></td></tr></table></figure>

<p>这时来看看前后两次的执行结果对比：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码JDK序列化后的字节数组长度：<span class="hljs-number">224</span><br>ZhuZi(id=<span class="hljs-number">1</span>, name=黄金竹子, grade=A级)<br>=============================================<br>JDK序列化后的字节数组长度：<span class="hljs-number">204</span><br>ZhuZi(id=<span class="hljs-number">1</span>, name=黄金竹子, grade=<span class="hljs-literal">null</span>)<br></code></pre></td></tr></table></figure>

<p>从结果可明显观察出，被<code>transient</code>修饰的属性，并不会参与序列化，<code>grade=null</code>，并且序列化后的字节长度也有明显变化。</p>
<hr>
<p>正因<code>Json</code>格式特别流行，随之演变出诸多<code>Json</code>库，通过使用这些库，可以让每位开发者更关注业务，屏蔽掉底层的序列化处理工作，<code>Java</code>中常用的库有：<code>Gson、Jackson、FastJson</code>，<code>SpringMVC</code>框架中默认使用<code>Jackson</code>来解析请求参数，不过我们这里以<code>FastJson</code>举例，首先引入相关依赖：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml">xml 体验AI代码助手 代码解读复制代码<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>	<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.alibaba<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>	<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>fastjson<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>	<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.0.27<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>依旧使用之前<code>ZhuZi</code>这个类，来演示<code>Json</code>与对象的互相转换（序列化与反序列化）：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>    <span class="hljs-type">ZhuZi</span> <span class="hljs-variable">zhuZi</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ZhuZi</span>(<span class="hljs-number">1</span>,<span class="hljs-string">&quot;黄金竹子&quot;</span>, <span class="hljs-string">&quot;A级&quot;</span>);<br><br>    <span class="hljs-comment">// 1. Java对象转Json字符串</span><br>    <span class="hljs-type">String</span> <span class="hljs-variable">json</span> <span class="hljs-operator">=</span> JSONObject.toJSONString(zhuZi);<br>    System.out.println(json);<br>    System.out.println(<span class="hljs-string">&quot;Json序列化后的体积：&quot;</span> + json.getBytes().length);<br><br>    <span class="hljs-comment">// 2. Json字符串转Java对象</span><br>    <span class="hljs-type">ZhuZi</span> <span class="hljs-variable">zhuZiJson</span> <span class="hljs-operator">=</span> JSONObject.parseObject(json, ZhuZi.class);<br>    System.out.println(zhuZiJson);<br>&#125;<br><span class="hljs-comment">/* 输出结果：</span><br><span class="hljs-comment"> *    &#123;&quot;grade&quot;:&quot;A级&quot;,&quot;id&quot;:1,&quot;name&quot;:&quot;黄金竹子&quot;&#125;</span><br><span class="hljs-comment"> *    Json序列化后的体积：45</span><br><span class="hljs-comment"> *    ZhuZi(id=1, name=黄金竹子, grade=A级)</span><br><span class="hljs-comment"> */</span><br></code></pre></td></tr></table></figure>

<p>使用起来特别简单，重点来看看输出结果里的体积，比<code>JDK</code>序列化后的体积，大概小了五倍左右~</p>
<p>接着来讲两个常用的场景，如何处理集合类型以及多泛型对象？</p>
<p>先来看看集合类型的<code>Json</code>序列化，如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testList</span><span class="hljs-params">()</span>&#123;<br>    List&lt;ZhuZi&gt; zhuZis = Arrays.asList(<br>            <span class="hljs-keyword">new</span> <span class="hljs-title class_">ZhuZi</span>(<span class="hljs-number">1</span>,<span class="hljs-string">&quot;黄金竹子&quot;</span>,<span class="hljs-string">&quot;A级&quot;</span>),<br>            <span class="hljs-keyword">new</span> <span class="hljs-title class_">ZhuZi</span>(<span class="hljs-number">2</span>, <span class="hljs-string">&quot;白玉竹子&quot;</span>, <span class="hljs-string">&quot;S级&quot;</span>));<br>    <span class="hljs-type">String</span> <span class="hljs-variable">json</span> <span class="hljs-operator">=</span> JSONArray.toJSONString(zhuZis);<br>    System.out.println(json);<br>    List&lt;ZhuZi&gt; zhuZisJson = JSONArray.parseArray(json, ZhuZi.class);<br>    System.out.println(zhuZisJson);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>这里可以直接用<code>JSONArray</code>类来转换，也可以用<code>JSONObject</code>类，反序列化时调用<code>parseArray()</code>方法即可。</p>
<p>接着来看看多泛型对象的处理，以<code>Map</code>为例，<code>Map</code>集合需要传入两个泛型，这时该如何反序列化呢？如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testMap</span><span class="hljs-params">()</span>&#123;<br>    Map&lt;String, ZhuZi&gt; zhuZiMap = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>    zhuZiMap.put(<span class="hljs-string">&quot;1&quot;</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">ZhuZi</span>(<span class="hljs-number">1</span>,<span class="hljs-string">&quot;黄金竹子&quot;</span>,<span class="hljs-string">&quot;A级&quot;</span>));<br>    zhuZiMap.put(<span class="hljs-string">&quot;2&quot;</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">ZhuZi</span>(<span class="hljs-number">2</span>, <span class="hljs-string">&quot;白玉竹子&quot;</span>, <span class="hljs-string">&quot;S级&quot;</span>));<br>    <span class="hljs-type">String</span> <span class="hljs-variable">json</span> <span class="hljs-operator">=</span> JSONObject.toJSONString(zhuZiMap);<br>    System.out.println(json);<br>    HashMap&lt;String, ZhuZi&gt; zhuZiMapJson = JSONObject<br>            .parseObject(json, <span class="hljs-keyword">new</span> <span class="hljs-title class_">TypeReference</span>&lt;HashMap&lt;String, ZhuZi&gt;&gt;() &#123;&#125;);<br>    System.out.println(zhuZiMapJson);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>序列化操作与之前没区别，重点是反序列化时，由于泛型有两个，就无法通过前面那种方式指定，如果直接传入<code>HashMap.class</code>，会被转换为<code>HashMap&lt;Object,Object&gt;</code>类型，想要正确的完成转换，则需要传入一个<code>TypeReference</code>对象，以此精准的告知反序列化类型。</p>
<hr>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7264414580774928443#heading-16">(十二)探索高性能通信与RPC框架基石：Json、ProtoBuf、Hessian序列化详解如今这个分布式风靡的时代，网 - 掘金</a></p>
<hr>
<p>除开前面提到的几种序列化方案外，相信看过<code>Dubbo</code>框架源码的小伙伴，一定还知道一种方案，即基于二进制实现<code>Hessian</code>，这是<code>Dubbo</code>中默认的序列化机制，用于服务提供者与消费者之间进行数据传输，这里咱们也简单过一下。</p>
<p><code>Hessian</code>和<code>JDK</code>原生的序列化技术，兼容度很高，相较于使用<code>ProtoBuf</code>而言，成本要低许多，首先导入一下依赖包：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml">xml 体验AI代码助手 代码解读复制代码<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>	<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.caucho<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>	<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>hessian<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>	<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>4.0.65<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>接着依旧基于最开始的<code>ZhuZi</code>实体类，来写一下测试代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">HessianDemo</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-comment">// 1. 序列化</span><br>        <span class="hljs-type">ZhuZi</span> <span class="hljs-variable">zhuZi</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ZhuZi</span>(<span class="hljs-number">1</span>,<span class="hljs-string">&quot;黄金竹子&quot;</span>, <span class="hljs-string">&quot;A级&quot;</span>);<br>        <span class="hljs-type">byte</span>[] serializeBytes = serialize(zhuZi);<br>        System.out.println(<span class="hljs-string">&quot;Hessian序列化后字节数组长度：&quot;</span> + serializeBytes.length);<br><br>        <span class="hljs-comment">// 2. 反序列化</span><br>        <span class="hljs-type">ZhuZi</span> <span class="hljs-variable">deserializeZhuZi</span> <span class="hljs-operator">=</span> deserialize(serializeBytes);<br>        System.out.println(deserializeZhuZi.toString());<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 序列化方法</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> zhuZi 需要序列化的对象</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 序列化后生成的字节流</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">byte</span>[] serialize(ZhuZi zhuZi) <span class="hljs-keyword">throws</span> IOException &#123;<br>        <span class="hljs-type">ByteArrayOutputStream</span> <span class="hljs-variable">bos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ByteArrayOutputStream</span>();<br>        <span class="hljs-type">Hessian2Output</span> <span class="hljs-variable">h2o</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Hessian2Output</span>(bos);<br>        h2o.writeObject(zhuZi);<br>        h2o.close();<br>        <span class="hljs-keyword">return</span> bos.toByteArray();<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 反序列化方法</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> bytes 字节序列（字节流）</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 实体类对象</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> ZhuZi <span class="hljs-title function_">deserialize</span><span class="hljs-params">(<span class="hljs-type">byte</span>[] bytes)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-type">ByteArrayInputStream</span> <span class="hljs-variable">bis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ByteArrayInputStream</span>(bytes);<br>        <span class="hljs-type">Hessian2Input</span> <span class="hljs-variable">h2i</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Hessian2Input</span>(bis);<br>        <span class="hljs-type">ZhuZi</span> <span class="hljs-variable">zhuZi</span> <span class="hljs-operator">=</span> (ZhuZi) h2i.readObject();<br>        h2i.close();<br>        <span class="hljs-keyword">return</span> zhuZi;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>上述代码对比最开始的<code>JDK</code>序列化方案，几乎一模一样，只是将输出/输入流对象，从<code>ObjectOutputStream、ObjectInputStream</code>换成了<code>Hessian2Output、Hessian2Input</code>，此时来看结果对比，如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码JDK序列化后的字节数组长度：<span class="hljs-number">224</span><br>ZhuZi(id=<span class="hljs-number">1</span>, name=黄金竹子, grade=A级)<br>=============================================<br>Hessian序列化后字节数组长度：<span class="hljs-number">70</span><br>ZhuZi(id=<span class="hljs-number">1</span>, name=黄金竹子, grade=A级)<br></code></pre></td></tr></table></figure>

<p>是不是特别惊讶？其余任何地方没有改变，仅用<code>Hessian2</code>替换掉<code>JDK</code>原生的<code>IO</code>流对象，结果码流体积竟然缩小了<code>3.2</code>倍！并且还完全保留了<code>JDK</code>序列化技术的特性，还支持多语言异构……，所以，这也是<code>Dubbo</code>使用<code>Hessian2</code>作为默认序列化技术的原因，不过<code>Dubbo</code>使用的是定制版，依赖如下：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml">xml 体验AI代码助手 代码解读复制代码<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.dubbo<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>dubbo-serialization-hessian2<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.2.0-beta.6<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>感兴趣的可以去看看<code>DecodeableRpcInvocation#decode()、encode()</code>这个两个方法，其中涉及到数据的编解码工作，默认采用<code>Hessian2</code>序列化技术~</p>
<h2 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h2><blockquote>
<p>2025 年 7 月 31 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7130952602350534693">(七)Java网络编程-IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析！如若你对于面试要求中的“ - 掘金</a></p>
<p>同步是指线程串行的依次执行，异步则是可以将自己要做的事情交给其他线程执行，然后主线程就能立马返回干其他事情。</p>
<p>  <code>BIO</code>就是Java的传统<code>IO</code>模型，与其相关的实现都位于<code>java.io</code>包下，其通信原理是客户端、服务端之间通过<code>Socket</code>套接字建立管道连接，然后从管道中获取对应的输入/输出流，最后利用输入/输出流对象实现发送/接收信息，案例如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-comment">// BIO服务端</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">BioServer</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> IOException &#123;<br>        System.out.println(<span class="hljs-string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;...BIO服务端启动...&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&quot;</span>);<br>        <span class="hljs-comment">// 1.定义一个ServerSocket服务端对象，并为其绑定端口号</span><br>        <span class="hljs-type">ServerSocket</span> <span class="hljs-variable">server</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ServerSocket</span>(<span class="hljs-number">8888</span>);<br>        <span class="hljs-comment">// 2.监听客户端Socket连接</span><br>        <span class="hljs-type">Socket</span> <span class="hljs-variable">socket</span> <span class="hljs-operator">=</span> server.accept();<br>        <span class="hljs-comment">// 3.从套接字中得到字节输入流并封装成输入流对象</span><br>        <span class="hljs-type">InputStream</span> <span class="hljs-variable">inputStream</span> <span class="hljs-operator">=</span> socket.getInputStream();<br>        <span class="hljs-type">BufferedReader</span> <span class="hljs-variable">readBuffer</span> <span class="hljs-operator">=</span><br>                <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedReader</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">InputStreamReader</span>(inputStream));<br>        <span class="hljs-comment">// 4.从Buffer中读取信息，如果读到信息则输出</span><br>        String msg;<br>        <span class="hljs-keyword">while</span> ((msg = readBuffer.readLine()) != <span class="hljs-literal">null</span>) &#123;<br>            System.out.println(<span class="hljs-string">&quot;收到信息：&quot;</span> + msg);<br>        &#125;<br>        <span class="hljs-comment">// 5.从套接字中获取字节输出流并封装成输出对象</span><br>        <span class="hljs-type">OutputStream</span> <span class="hljs-variable">outputStream</span> <span class="hljs-operator">=</span> socket.getOutputStream();<br>        <span class="hljs-type">PrintStream</span> <span class="hljs-variable">printStream</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">PrintStream</span>(outputStream);<br>        <span class="hljs-comment">// 6.通过输出对象往服务端传递信息</span><br>        printStream.println(<span class="hljs-string">&quot;Hi！我是竹子~&quot;</span>);<br>        <span class="hljs-comment">// 7.发送后清空输出流中的信息</span><br>        printStream.flush();<br>        <span class="hljs-comment">// 8.使用完成后关闭流对象与套接字</span><br>        outputStream.close();<br>        inputStream.close();<br>        socket.close();<br>        inputStream.close();<br>        outputStream.close();<br>        socket.close();<br>        server.close();<br>    &#125;<br>&#125;<br><br><span class="hljs-comment">// BIO客户端</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">BioClient</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> IOException &#123;<br>        System.out.println(<span class="hljs-string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;...BIO客户端启动...&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&quot;</span>);<br>        <span class="hljs-comment">// 1.创建Socket并根据IP地址与端口连接服务端</span><br>        <span class="hljs-type">Socket</span> <span class="hljs-variable">socket</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Socket</span>(<span class="hljs-string">&quot;127.0.0.1&quot;</span>, <span class="hljs-number">8888</span>);<br>        <span class="hljs-comment">// 2.从Socket对象中获取一个字节输出流并封装成输出对象</span><br>        <span class="hljs-type">OutputStream</span> <span class="hljs-variable">outputStream</span> <span class="hljs-operator">=</span> socket.getOutputStream();<br>        <span class="hljs-type">PrintStream</span> <span class="hljs-variable">printStream</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">PrintStream</span>(outputStream);<br>        <span class="hljs-comment">// 3.通过输出对象往服务端传递信息</span><br>        printStream.println(<span class="hljs-string">&quot;Hello！我是熊猫~&quot;</span>);<br>        <span class="hljs-comment">// 4.通过下述方法告诉服务端已经完成发送，接下来只接收消息</span><br>        socket.shutdownOutput();<br>        <span class="hljs-comment">// 5.从套接字中获取字节输入流并封装成输入对象</span><br>        <span class="hljs-type">InputStream</span> <span class="hljs-variable">inputStream</span> <span class="hljs-operator">=</span> socket.getInputStream();<br>        <span class="hljs-type">BufferedReader</span> <span class="hljs-variable">readBuffer</span> <span class="hljs-operator">=</span><br>                <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedReader</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">InputStreamReader</span>(inputStream));<br>        <span class="hljs-comment">// 6.通过输入对象从Buffer读取信息</span><br>        String msg;<br>        <span class="hljs-keyword">while</span> ((msg = readBuffer.readLine()) != <span class="hljs-literal">null</span>) &#123;<br>            System.out.println(<span class="hljs-string">&quot;收到信息：&quot;</span> + msg);<br>        &#125;<br>        <span class="hljs-comment">// 7.发送后清空输出流中的信息</span><br>        printStream.flush();<br>        <span class="hljs-comment">// 8.使用完成后关闭流对象与套接字</span><br>        outputStream.close();<br>        inputStream.close();<br>        socket.close();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>分别启动<code>BioServer、BioClient</code>类，运行结果如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-comment">// ------服务端---------</span><br>&gt;&gt;&gt;&gt;&gt;&gt;&gt;...BIO服务端启动...&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;<br>收到信息：Hello！我是熊猫~<br><br><span class="hljs-comment">// ------客户端---------</span><br>&gt;&gt;&gt;&gt;&gt;&gt;&gt;...BIO客户端启动...&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;<br>收到信息：Hi！我是竹子~<br></code></pre></td></tr></table></figure>

<p>观察如上结果，其实执行过程原理很简单：</p>
<ul>
<li>①服务端启动后会执行<code>accept()</code>方法等待客户端连接到来。</li>
<li>②客户端启动后会通过<code>IP</code>及端口，与服务端通过<code>Socket</code>套接字建立连接。</li>
<li>③然后双方各自从套接字中获取输入/输出流，并通过流对象发送/接收消息。</li>
</ul>
<p>大体过程如下：<br> <img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f31626dacb1041f0a3ae8d0a523ed5a7~tplv-k3u1fbpfcp-jj-mark:3024:0:0:0:q75.awebp#?w=849&h=328&s=112118&e=png&b=fdfcfc" alt="Java-BIO"><br> 在上述<code>Java-BIO</code>的通信过程中，如若客户端一直没有发送消息过来，服务端则会一直等待下去，从而服务端陷入阻塞状态。同理，由于客户端也一直在等待服务端的消息，如若服务端一直未响应消息回来，客户端也会陷入阻塞状态。</p>
<hr>
<p>简单了解了选择器的基础概念后，那如何使用它实现非阻塞模型呢？如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-comment">// ----NIO服务端实现--------</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">NioServer</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        System.out.println(<span class="hljs-string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;...NIO服务端启动...&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&quot;</span>);<br>        <span class="hljs-comment">// 1.创建服务端通道、选择器与字节缓冲区</span><br>        <span class="hljs-type">ServerSocketChannel</span> <span class="hljs-variable">ssc</span> <span class="hljs-operator">=</span> ServerSocketChannel.open();<br>        <span class="hljs-type">Selector</span> <span class="hljs-variable">selector</span> <span class="hljs-operator">=</span> Selector.open();<br>        <span class="hljs-type">ByteBuffer</span> <span class="hljs-variable">buffer</span> <span class="hljs-operator">=</span> ByteBuffer.allocate(<span class="hljs-number">1024</span>);<br>        <span class="hljs-comment">// 2.为服务端绑定IP地址+端口</span><br>        ssc.bind(<span class="hljs-keyword">new</span> <span class="hljs-title class_">InetSocketAddress</span>(<span class="hljs-string">&quot;127.0.0.1&quot;</span>,<span class="hljs-number">8888</span>));<br>        <span class="hljs-comment">// 3.将服务端设置为非阻塞模式，同时绑定接收事件注册到选择器</span><br>        ssc.configureBlocking(<span class="hljs-literal">false</span>);<br>        ssc.register(selector, SelectionKey.OP_ACCEPT);<br>        <span class="hljs-comment">// 4.通过选择器轮询所有已就绪的通道</span><br>        <span class="hljs-keyword">while</span> (selector.select() &gt; <span class="hljs-number">0</span>)&#123;<br>            <span class="hljs-comment">// 5.获取当前选择器上注册的通道中所有已经就绪的事件</span><br>            Iterator&lt;SelectionKey&gt; iterator = selector.selectedKeys().iterator();<br>            <span class="hljs-comment">// 6.遍历得到的所有事件，并根据事件类型进行处理</span><br>            <span class="hljs-keyword">while</span> (iterator.hasNext())&#123;<br>                <span class="hljs-type">SelectionKey</span> <span class="hljs-variable">next</span> <span class="hljs-operator">=</span> iterator.next();<br>                <span class="hljs-comment">// 7.如果是接收事件就绪，那则获取对应的客户端连接</span><br>                <span class="hljs-keyword">if</span> (next.isAcceptable())&#123;<br>                    <span class="hljs-type">SocketChannel</span> <span class="hljs-variable">channel</span> <span class="hljs-operator">=</span> ssc.accept();<br>                    <span class="hljs-comment">// 8.将获取到的客户端连接置为非阻塞模式，绑定事件并注册到选择器上</span><br>                    channel.configureBlocking(<span class="hljs-literal">false</span>);<br>                    <span class="hljs-type">int</span> <span class="hljs-variable">event</span> <span class="hljs-operator">=</span> SelectionKey.OP_READ | SelectionKey.OP_WRITE;<br>                    channel.register(selector,event);<br>                    System.out.println(<span class="hljs-string">&quot;客户端连接：&quot;</span> + channel.getRemoteAddress());<br>                &#125;<br>                <span class="hljs-comment">// 9.如果是读取事件就绪，则先获取对应的通道连接</span><br>                <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(next.isReadable())&#123;<br>                    <span class="hljs-type">SocketChannel</span> <span class="hljs-variable">channel</span> <span class="hljs-operator">=</span> (SocketChannel)next.channel();<br>                    <span class="hljs-comment">// 10.然后从对应的通道中，将数据读取到缓冲区并输出</span><br>                    <span class="hljs-type">int</span> <span class="hljs-variable">len</span> <span class="hljs-operator">=</span> -<span class="hljs-number">1</span>;<br>                    <span class="hljs-keyword">while</span> ((len = channel.read(buffer)) &gt; <span class="hljs-number">0</span>)&#123;<br>                        buffer.flip();<br>                        System.out.println(<span class="hljs-string">&quot;收到信息：&quot;</span> +<br>                                <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(buffer.array(),<span class="hljs-number">0</span>,buffer.remaining()));<br>                    &#125;<br>                    buffer.clear();<br>                &#125;<br>            &#125;<br>            <span class="hljs-comment">// 11.将已经处理后的事件从选择器上移除（选择器不会自动移除）</span><br>            iterator.remove();<br>        &#125;<br>    &#125;<br>&#125;<br><br><span class="hljs-comment">// ----NIO客户端实现--------</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">NioClient</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        System.out.println(<span class="hljs-string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;...NIO客户端启动...&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&quot;</span>);<br>        <span class="hljs-comment">// 1.创建一个TCP类型的通道并指定地址建立连接</span><br>        <span class="hljs-type">SocketChannel</span> <span class="hljs-variable">channel</span> <span class="hljs-operator">=</span> SocketChannel.open(<br>                <span class="hljs-keyword">new</span> <span class="hljs-title class_">InetSocketAddress</span>(<span class="hljs-string">&quot;127.0.0.1&quot;</span>,<span class="hljs-number">8888</span>));<br>        <span class="hljs-comment">// 2.将通道置为非阻塞模式</span><br>        channel.configureBlocking(<span class="hljs-literal">false</span>);<br>        <span class="hljs-comment">// 3.创建字节缓冲区，并写入要传输的消息数据</span><br>        <span class="hljs-type">ByteBuffer</span> <span class="hljs-variable">buffer</span> <span class="hljs-operator">=</span> ByteBuffer.allocate(<span class="hljs-number">1024</span>);<br>        <span class="hljs-type">String</span> <span class="hljs-variable">msg</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;我是熊猫！&quot;</span>;<br>        buffer.put(msg.getBytes());<br>        <span class="hljs-comment">// 4.将缓冲区切换为读取模式</span><br>        buffer.flip();<br>        <span class="hljs-comment">// 5.将带有数据的缓冲区写入通道，利用通道传输数据</span><br>        channel.write(buffer);<br>        <span class="hljs-comment">// 6.传输完成后情况缓冲区、关闭通道</span><br>        buffer.clear();<br>        channel.close();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>在如上案例中，即实现了一个最简单的<code>NIO</code>服务端与客户端通信的案例，重点要注意：注册到选择器上的通道都必须要为非阻塞模型，同时通过缓冲区传输数据时，必须要调用<code>flip()</code>方法切换为读取模式。</p>
<p>OK<del>，最后简单叙述一下缓冲区、通道、选择器三者关系：<br> ![多路复用模型](<a target="_blank" rel="noopener" href="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/faba15014ec648dab401d7cfdba85e9f">https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/faba15014ec648dab401d7cfdba85e9f</a></del>tplv-k3u1fbpfcp-jj-mark:3024:0:0:0:q75.awebp#?w=685&amp;h=508&amp;s=142945&amp;e=png&amp;b=fcfcfc)<br> 如上图所示，每个客户端连接本质上对应着一个<code>Channel</code>通道，而一个通道也有一个与之对应的<code>Buffer</code>缓冲区，在客户端尝试连接服务端时，会利用通道将其注册到选择器上，这个选择器则会有一条对应的线程。在开始工作后，选择器会根据不同的事件在各个通道上切换，对于已就绪的数据会基于通道与<code>Buffer</code>缓冲区进行读写操作。</p>
<p>作者：竹子爱熊猫<br>链接：<a target="_blank" rel="noopener" href="https://juejin.cn/post/7130952602350534693">https://juejin.cn/post/7130952602350534693</a><br>来源：稀土掘金<br>著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。</p>
<h2 id="跨域"><a href="#跨域" class="headerlink" title="跨域"></a>跨域</h2><blockquote>
<p>2025 年 7 月 29 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7407075949509869609#heading-6">(十二)漫谈分布式之接口设计上篇：写出一个优秀的接口我们需要考虑什么？作为一名后端开发，你会写接口吗？有人说你这不废话吗 - 掘金</a></p>
<p>跨域问题的产生背景是浏览器的同源策略（<code>Same-Origin Policy</code>），同源策略是浏览器的一种安全机制，它限制了从同一个源加载的文档或脚本，如何与来自不同源的资源进行交互。这里的源（<code>origin</code>）是指协议、域名、端口号组成的唯一标识，如果出现两个地址，这三者组合起来对比不一致，就代表着不同源。</p>
<p>如何解决不同源的跨域问题呢？也很简单，在<code>SpringBoot</code>里通过一个<code>@CrossOrigin</code>注解就能搞定，如果嫌挨个<code>Controller</code>加注解麻烦，也可以通过实现<code>WebMvcConfigurer</code>接口，并重写<code>addCorsMappings()</code>方法来全局配置跨域，即：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-meta">@Configuration</span>  <br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CorsConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> &#123;  <br>  <br>    <span class="hljs-meta">@Override</span>  <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addCorsMappings</span><span class="hljs-params">(CorsRegistry registry)</span> &#123;  <br>        registry<br>        <span class="hljs-comment">// 代表允许所有路径跨域</span><br>        .addMapping(<span class="hljs-string">&quot;/**&quot;</span>)<br>                <span class="hljs-comment">// 允许来自http://xxx.com的请求跨域  </span><br>                .allowedOrigins(<span class="hljs-string">&quot;http://xxx.com&quot;</span>)<br>                <span class="hljs-comment">// 允许这些HTTP方法跨域</span><br>                .allowedMethods(<span class="hljs-string">&quot;GET&quot;</span>, <span class="hljs-string">&quot;POST&quot;</span>, <span class="hljs-string">&quot;PUT&quot;</span>, <span class="hljs-string">&quot;DELETE&quot;</span>)<br>                <span class="hljs-comment">// 允许任何头跨域</span><br>                .allowedHeaders(<span class="hljs-string">&quot;*&quot;</span>)<br>                <span class="hljs-comment">// 允许发送Cookie</span><br>                .allowCredentials(<span class="hljs-literal">true</span>)<br>                <span class="hljs-comment">// 预检请求的缓存时间（秒）</span><br>                .maxAge(<span class="hljs-number">3600</span>);<br>    &#125;  <br>&#125;<br></code></pre></td></tr></table></figure>

<p>当然，解决跨域问题的本质，是往响应头里塞几个字段，即：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码response.setHeader(<span class="hljs-string">&quot;Access-Control-Allow-Origin&quot;</span>, <span class="hljs-string">&quot;*&quot;</span>);  <br>response.setHeader(<span class="hljs-string">&quot;Access-Control-Allow-Methods&quot;</span>, <span class="hljs-string">&quot;POST, GET, OPTIONS, DELETE&quot;</span>);  <br>response.setHeader(<span class="hljs-string">&quot;Access-Control-Max-Age&quot;</span>, <span class="hljs-string">&quot;3600&quot;</span>);  <br>response.setHeader(<span class="hljs-string">&quot;Access-Control-Allow-Headers&quot;</span>, <span class="hljs-string">&quot;x-requested-with, authorization&quot;</span>); <br></code></pre></td></tr></table></figure>

<p>因此，<strong>只要能够改写响应头的地方都可以解决跨域问题</strong>，所以市面上才会出现那么多解决跨域的方式：</p>
<ul>
<li>使用<code>@CrossOrigin</code>注解解决跨域问题；</li>
<li>实现<code>WebMvcConfigurer</code>接口解决跨域问题；</li>
<li>通过自定义<code>Filter</code>过滤器解决跨域问题；</li>
<li>通过<code>Response</code>对象实现跨域问题；</li>
<li>通过实现<code>ResponseBodyAdvice</code>接口解决跨域问题；</li>
<li>通过设置<a target="_blank" rel="noopener" href="https://juejin.cn/post/7112826654291918855#heading-11">Nginx配置</a>解决跨域问题；</li>
<li>……</li>
</ul>
<p>这么多种跨域解决方案听起来很唬人，但只要是服务端解决的跨域问题，殊途同归都是改写响应头罢了。</p>
<h2 id="并发安全"><a href="#并发安全" class="headerlink" title="并发安全"></a>并发安全</h2><p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7407277812262174739">(十三)漫谈分布式之接口设计下篇：设计一个优秀写接口的13条黄金法则！引言 在前面《写好一个接口需要考虑什么？》这篇文章 - 掘金</a></p>
<blockquote>
<p>2025 年 7 月 29 日</p>
</blockquote>
<p>什么情况下需要考虑线程安全问题？我听到过最多的回答就是下单扣库存，每当我让候选人换一个例子的时候，多数人就很难继续回答了，那么到底什么情况下会线程不安全呢？来看个例子：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">joinGroup</span><span class="hljs-params">(Long groupId)</span> &#123;<br>    <span class="hljs-comment">// 查询拼团人数是否已满</span><br>    <span class="hljs-type">boolean</span> <span class="hljs-variable">flag</span> <span class="hljs-operator">=</span> groupService.groupBuyingFull(groupId);<br>    <span class="hljs-keyword">if</span>(flag) &#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BusinessException</span>(<span class="hljs-string">&quot;拼团人数已满员！&quot;</span>);<br>    &#125;<br>    <span class="hljs-comment">// 如果拼团人数未满，则加入拼团</span><br>    groupService.insertJoinGroupRecord(UserHolder.getUserId(), groupId);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>这是一段极其简单的伪代码，首先会根据团<code>ID</code>去查询是否已满员，如果满员则返回拼团失败，反之则插入拼团记录返回拼团成功。请问各位小伙伴，这段代码是否存在线程安全问题呢？大家可以认真思考片刻……</p>
<p>答案是存在，<code>Why</code>？很简单，因为后面的写操作，依赖于前面的查操作，而之前并发编程专栏曾提到过一个定律：<strong>同一时刻多条线程对共享资源进行非原子性操作，则有可能产生线程安全问题</strong>，而这个例子恰恰满足条件，拆开分析下：</p>
<ul>
<li>多线程（条件1）：两个用户同时请求这个拼团接口，就会转变为两条线程执行；</li>
<li>共享资源（条件2）：对于两个请求而言，数据库里的拼团记录是共享可见的；</li>
<li>非原子性操作（条件3）：这里先查询、再插入，分为两步执行，并非一起执行的。</li>
</ul>
<p>综上，这个场景完全符合线程安全问题的产生背景，比如目前”小竹搬砖团“还剩最后一个名额，两个用户同时申请加入该团，代表两个请求几乎是同时过来的，那么在执行”拼团人数是否已满查询“时，这时看到的结果都是<code>false</code>，因为此时最后一个名额还在，然后两个请求都会执行<code>insertJoinGroupRecord()</code>方法，最终导致最后一个名额被两人同时拿到。</p>
<p>那该如何解决这个问题呢？打破构成线程不安全的三个条件即可：</p>
<ul>
<li>①破坏多线程条件：同一时刻，只允许一条线程对共享资源进行非原子性操作；</li>
<li>②破坏共享资源条件：同一时刻多条线程对局部资源进行非原子性操作；</li>
<li>③破坏非原子性条件：同一时刻多条线程对共享资源进行原子性操作。</li>
</ul>
<p>说简单一点就是方案一就是加锁，方案二在这个场景里实现不了，方案三可以理解成<code>CAS</code>无锁自旋，即乐观锁方案。不过最常用的还是加锁，如果你是单体应用，则可使用<a target="_blank" rel="noopener" href="https://juejin.cn/post/6977744582725681182">synchronized关键字</a>、<a target="_blank" rel="noopener" href="https://juejin.cn/post/6977746796093112333">ReetrantLock可重入锁</a>这种单机锁，具体怎么用可以参考之前<a target="_blank" rel="noopener" href="https://juejin.cn/post/7236213437800890423">《单体项目并发漏洞》</a>这篇文章，这里就不做重复赘述。如果是分布式集群部署的环境，则可以使用<a target="_blank" rel="noopener" href="https://juejin.cn/post/7038473714970656775">基于Redis、Zookeeper实现的分布式锁</a>，用起来都不难~</p>
<p>但不管是单机锁也好，分布式锁也罢，其实核心思想都是一样的，底层的本质就是一个对所有线程可见的锁标识，谁先将其改为<code>1</code>就代表先拿到锁，拿到锁的线程可以先执行，执行结束后再把锁放掉，其余线程也可以继续抢占锁资源了。</p>
<p>OK，再来看个问题，还是前面的代码，假设这里是单体应用，现在我对其加一把锁：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">joinGroup</span><span class="hljs-params">(Long groupId)</span> &#123;<br>    <span class="hljs-comment">// 查询拼团人数是否已满</span><br>    <span class="hljs-type">boolean</span> <span class="hljs-variable">flag</span> <span class="hljs-operator">=</span> groupService.groupBuyingFull(groupId);<br>    <span class="hljs-keyword">if</span>(flag) &#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BusinessException</span>(<span class="hljs-string">&quot;拼团人数已满员！&quot;</span>);<br>    &#125;<br>    <span class="hljs-comment">// 如果拼团人数未满，则加入拼团</span><br>    <span class="hljs-keyword">synchronized</span>(<span class="hljs-built_in">this</span>) &#123;<br>        groupService.insertJoinGroupRecord(UserHolder.getUserId(), groupId);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>因为写数据存在线程安全问题，所以我用<code>synchronized</code>将其包裹，这段代码有没有问题？答案是仍然有问题，因为这里锁的范围不够，还要将前面的查询一起放进<code>synchronized</code>才对。最后，还有个细节就是锁的维度，这里是基于<code>this</code>加锁，这时就算不同的团购拼团，也会竞争同一把锁，最终导致性能低效，怎么办才好呢？直接基于团购<code>ID</code>加锁就好啦，不过里面有些细节坑，不了解的可参考前面给出的文章链接。</p>
<h2 id="多线程优化"><a href="#多线程优化" class="headerlink" title="多线程优化"></a>多线程优化</h2><blockquote>
<p>2025 年 7 月 29 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7407277812262174739">(十三)漫谈分布式之接口设计下篇：设计一个优秀写接口的13条黄金法则！引言 在前面《写好一个接口需要考虑什么？》这篇文章 - 掘金</a></p>
<p>前面的批处理，是优化写接口性能的一种方式，而当接口出现性能问题时，多线程技术永远是解决问题的一大利器，不过许多人对多线程的适用并不熟练，这里先来说明异步和并发（并行）的区别。</p>
<ul>
<li>异步：将对应的任务递交给其他线程后，不需要等待结果返回，可以直接对外响应；</li>
<li>并发：通过多条线程来提升效率，提交任务的主线程需要等待结果返回，只是优化性能。</li>
</ul>
<p>异步和并发两个概念彼此兼容，所以许多人有点犯迷糊，结合生活来理解，比如现在我要搬一百块砖头，可是我一个人搬的太慢了，所以想着多喊几个人来帮忙，于是我找到<code>X、Y、Z</code>，并叫它们一起来搬砖提升效率，这时我会等它们搬完，这就是并发的概念。</p>
<blockquote>
<p>如果我找到<code>X、Y、Z</code>把搬砖任务丢给了它们，不管它们有没有搬完，然后我就自己走了，这就是异步的概念。</p>
</blockquote>
<p>综上，当接口写入性能较差时，咱们确实可以通过多线程来优化性能，可到底要用多线程来并发处理，还是用它来异步处理呢？这就取决于你实际的业务场景，来看例子：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">public</span> String <span class="hljs-title function_">writeBigData</span><span class="hljs-params">(List&lt;Panda&gt; pandas)</span> &#123;<br>    pandaService.writePandas(pandas);<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;写入成功&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>这是一个写熊猫数据的接口，假设外部传入了<code>10W</code>条熊猫数据需要落库，单线程处理的效率过低，这时用多线程优化，可以这么写：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">public</span> String <span class="hljs-title function_">writeBigData</span><span class="hljs-params">(List&lt;Panda&gt; pandas)</span> &#123;<br>    threadPool.submit(() -&gt; &#123;<br>        pandaService.writePandas(pandas);<br>    &#125;);<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;写入成功&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>这段代码中，主线程将写熊猫数据的任务，丢给线程池后立马返回了，这是典型的异步写法，再来看例子：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">public</span> String <span class="hljs-title function_">writeBigData</span><span class="hljs-params">(List&lt;Panda&gt; pandas)</span> &#123;<br>    <span class="hljs-comment">// 先对数据进行切分，分割为1000一批的数据</span><br>    List&lt;List&lt;Panda&gt;&gt; partitions = ListUtils.partition(pandas, <span class="hljs-number">1000</span>);<br>    <span class="hljs-comment">// 定义计数器</span><br>    <span class="hljs-type">AtomicInteger</span> <span class="hljs-variable">count</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AtomicInteger</span>(<span class="hljs-number">0</span>);<br>    <span class="hljs-comment">// 循环所有批次，将任务提交给线程池</span><br>    <span class="hljs-keyword">for</span> (List&lt;Panda&gt; partition : partitions) &#123;<br>        threadPool.submit(() -&gt; &#123;<br>            pandaService.writePandas(partition);<br>            count.incrementAndGet();<br>        &#125;);<br>    &#125;<br>    <br>    <span class="hljs-comment">// 模拟阻塞（实际要通过Future来阻塞等待执行结果）</span><br>    <br>    <span class="hljs-comment">// 如果写入成功的批数，等于划分出的批数，返回写入成功</span><br>    <span class="hljs-keyword">if</span> (count.get() == partitions.size()) &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;写入成功&quot;</span>;<br>    &#125;<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;写入失败&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>再来看这种写法，首先对传入的熊猫集合进行了分批，将数据分为多个<code>1000</code>条的小批次，而后遍历拆分后的批次列表，将拆分的每批数据都丢给了线程池去执行。再来看外部的主线程，任务投递给线程池后并未立马返回，而是在等待所有批次的执行结果，只有当所有批次都完成写入后，才真正向调用方返回了写入成功。</p>
<p>通过上面的案例，我们演示了多线程异步和并发的用法，具体的场景中诸位要用哪种方式，可以结合业务场景来做抉择。</p>
<h2 id="函数式接口"><a href="#函数式接口" class="headerlink" title="函数式接口"></a>函数式接口</h2><blockquote>
<p>2025 年 7 月 29 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7372863316912078889#heading-4">詹姆斯·高斯林：整整十年过去了！你小子还不会用我的Java8？距离Java8的发布，至今已过去了整整十年，而JDK22在 - 掘金</a></p>
<p>归功于类型推导机制，我们可以在Java8中使用<code>lambda</code>来使得代码简洁化，不过经过上阶段的学习会发现一个致命问题：<strong>每写一个Lambda表达式，就需要单独定义一个接口，如果真是这样，Lambda省下来的代码，又全都在接口定义上补回去了，这有点拆东墙补西墙的味道</strong>。</p>
<p><code>JDK</code>官方显然也想到了这一点，所以提供了一个<code>java.util.function</code>包，这里面定义了一系列可复用的、使用频率较高的函数式接口，以此避免日常开发过程中重复定义类似的接口，可到底啥叫做函数式接口？<strong>函数式接口是Java8新增的一种接口定义</strong>。</p>
<p>但说到底，函数式接口跟普通的接口写法都一样，唯一的区别在于：<strong>函数式接口就是一个只具有一个抽象方法的特殊接口（可以定义多个方法，但其他的方法只能是default或static）</strong>。同时，也可以用<code>@FunctionalInterface</code>注解来将一个接口声明函数式接口，不过这个注解加不加，都不影响表达式的执行，仅仅只是起到编译校验的作用，如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-meta">@FunctionalInterface</span> <br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">A</span> &#123;     <br>    <span class="hljs-keyword">void</span> <span class="hljs-title function_">a</span><span class="hljs-params">()</span>;<br>    <span class="hljs-keyword">default</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">b</span><span class="hljs-params">()</span> &#123;&#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>这个接口只有一个抽象方法，所以编译能正常通过，再看个反例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-meta">@FunctionalInterface</span> <br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">B</span> &#123;     <br>      <span class="hljs-keyword">void</span> <span class="hljs-title function_">a</span><span class="hljs-params">()</span>;     <br>      <span class="hljs-keyword">void</span> <span class="hljs-title function_">b</span><span class="hljs-params">()</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>这个接口有多个抽象方法，所以编译会提示错误。OK，接着来看看<code>java.util.function</code>包下提供的函数式接口，这里列几个常用：</p>
<table>
<thead>
<tr>
<th>接口</th>
<th>描述</th>
<th>示例</th>
</tr>
</thead>
<tbody><tr>
<td>Supplier</td>
<td>无入参，返回一个结果</td>
<td><code>() -&gt; &#123;return 0;&#125;;</code></td>
</tr>
<tr>
<td>Function</td>
<td>单个入参，返回一个结果</td>
<td><code>i -&gt; &#123;return i * 100;&#125;;</code></td>
</tr>
<tr>
<td>Consumer</td>
<td>单个入参，无返回结果</td>
<td><code>str -&gt; System.out.println(str);</code></td>
</tr>
<tr>
<td>Predicate</td>
<td>单个入参，返回一个布尔值结果</td>
<td><code>str -&gt; &#123;return str.isEmpty();&#125;</code></td>
</tr>
<tr>
<td>……</td>
<td>……</td>
<td>……</td>
</tr>
</tbody></table>
<p>当然，还有一系列和命名上述类似，但是以<code>Bi……</code>开头的函数式接口，例如<code>BiFunction</code>，其实这就是前面的增强版，只是支持两个入参罢了。好了，那么我们该如何使用JDK自带的这些函数式接口呢？来个例子感受一下。</p>
<blockquote>
<p>需求：实现两个数字的加减乘除计算。</p>
</blockquote>
<p>如果用之前的思维来实现，要么就分别定义加、减、乘、除四个方法，要么就传一个运算符，在用<code>if</code>或<code>switch</code>判断，以此实现不同的计算逻辑，但现在可以用<code>lambda</code>表达式来换一种实现方式：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-comment">/*</span><br><span class="hljs-comment"> * 计算两个数字的方法</span><br><span class="hljs-comment"> * */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-title function_">calculate</span><span class="hljs-params">(<span class="hljs-type">int</span> x, <span class="hljs-type">int</span> y, BiFunction&lt;Integer, Integer, Integer&gt; calculateModel)</span> &#123;<br>     <span class="hljs-keyword">return</span> calculateModel.apply(x, y);<br> &#125;<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>     <span class="hljs-type">int</span> <span class="hljs-variable">a</span> <span class="hljs-operator">=</span> <span class="hljs-number">4</span>;<br>     <span class="hljs-type">int</span> <span class="hljs-variable">b</span> <span class="hljs-operator">=</span> <span class="hljs-number">2</span>;<br><br>     <span class="hljs-comment">// 加法计算</span><br>     <span class="hljs-type">int</span> <span class="hljs-variable">result1</span> <span class="hljs-operator">=</span> calculate(a, b, (x, y) -&gt; x + y);<br>     System.out.println(<span class="hljs-string">&quot;两数之和：&quot;</span> + result1);<br><br>     <span class="hljs-comment">// 减法计算</span><br>     <span class="hljs-type">int</span> <span class="hljs-variable">result2</span> <span class="hljs-operator">=</span> calculate(a, b, (x, y) -&gt; x - y);<br>     System.out.println(<span class="hljs-string">&quot;两数之差：&quot;</span> + result2);<br><br>     <span class="hljs-comment">// 乘法计算</span><br>     <span class="hljs-type">int</span> <span class="hljs-variable">result3</span> <span class="hljs-operator">=</span> calculate(a, b, (x, y) -&gt; x * y);<br>     System.out.println(<span class="hljs-string">&quot;两数之积：&quot;</span> + result3);<br><br>     <span class="hljs-comment">// 除法计算</span><br>     <span class="hljs-type">int</span> <span class="hljs-variable">result4</span> <span class="hljs-operator">=</span> calculate(a, b, (x, y) -&gt; x / y);<br>     System.out.println(<span class="hljs-string">&quot;两数之商：&quot;</span> + result4);<br> &#125;<br></code></pre></td></tr></table></figure>

<p>上述代码的运行结果如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码两数之和：<span class="hljs-number">6</span><br>两数之差：<span class="hljs-number">2</span><br>两数之积：<span class="hljs-number">8</span><br>两数之商：<span class="hljs-number">8</span><br></code></pre></td></tr></table></figure>

<p>这个例子中，我们基于JDK提供的函数式接口，完成了一个小需求的开发。函数式接口和<code>lambda</code>表达式结合，能使得程序更加灵活，允许将一个函数作为参数传递。</p>
<h2 id="Lambda-表达式"><a href="#Lambda-表达式" class="headerlink" title="Lambda 表达式"></a>Lambda 表达式</h2><blockquote>
<p>2025 年 7 月 29 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7372863316912078889#heading-4">詹姆斯·高斯林：整整十年过去了！你小子还不会用我的Java8？距离Java8的发布，至今已过去了整整十年，而JDK22在 - 掘金</a></p>
<p>在JDK1.8之前，一个方法能接收的入参类型，都只能是“值类型”，要么是基本数据类型，要么就是一个引用对象，如果想要将另一个方法作为入参怎么办？在之前的版本中只能通过匿名内部类来拐着弯实现，不过匿名内部类依赖于接口，所以先定义一个接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">ZhuZiCallback</span> &#123;<br>    <span class="hljs-comment">/*</span><br><span class="hljs-comment">    * 回调方法</span><br><span class="hljs-comment">    * */</span><br>    <span class="hljs-keyword">void</span> <span class="hljs-title function_">callback</span><span class="hljs-params">(ZhuZi zhuZi)</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>下面来看如何将这个回调方法作为入参传递给一个方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-meta">@Data</span><br><span class="hljs-meta">@AllArgsConstructor</span><br><span class="hljs-meta">@NoArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ZhuZi</span> &#123;<br>    <span class="hljs-keyword">private</span> Long id;<br>    <span class="hljs-keyword">private</span> String name;<br>&#125;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Test</span> &#123;<br>    <span class="hljs-comment">/*</span><br><span class="hljs-comment">    * 创建完对象后，触发指定的回调逻辑</span><br><span class="hljs-comment">    * */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">create</span><span class="hljs-params">(<span class="hljs-type">long</span> id, String name, ZhuZiCallback zhuZiCallback)</span> &#123;<br>        <span class="hljs-type">ZhuZi</span> <span class="hljs-variable">zhuZi</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ZhuZi</span>(id, name);<br>        zhuZiCallback.callback(zhuZi);<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        Test.create(<span class="hljs-number">88888888</span>, <span class="hljs-string">&quot;竹子爱熊猫&quot;</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">ZhuZiCallback</span>() &#123;<br>            <span class="hljs-meta">@Override</span><br>            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">callback</span><span class="hljs-params">(ZhuZi zhuZi)</span> &#123;<br>                System.out.println(<span class="hljs-string">&quot;我是创建完竹子对象后的回调，创建的对象为：&quot;</span> + zhuZi);<br>            &#125;<br>        &#125;);<br>    &#125;<br>&#125;<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment">* 执行结果：</span><br><span class="hljs-comment">*   我是创建完竹子对象后的回调，创建的对象为：ZhuZi(id=88888888, name=竹子爱熊猫)</span><br><span class="hljs-comment">* */</span><br></code></pre></td></tr></table></figure>

<p>来看上面这个回调事件的例子，其中的<code>ZhuZiCallback</code>是一种动作，我们真正关心的只有<code>callback()</code>方法里的逻辑而已，可是<code>Java</code>中不支持直接传递函数，所以为了将这个回调方法传递给要执行的<code>create()</code>方法，必须得<code>new</code>一个匿名内部类，写起来费劲不说，还不美观！</p>
<p>到了<code>JDK1.8</code>，就可以直接用<code>Lambda</code>表达式来代替，上述代码可以优化成：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>     Test.create(<span class="hljs-number">88888888</span>, <span class="hljs-string">&quot;竹子爱熊猫&quot;</span>, zhuZi -&gt; &#123;<br>        System.out.println(<span class="hljs-string">&quot;我是创建完竹子对象后的回调，创建的对象为：&quot;</span> + zhuZi);<br>    &#125;);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>这样写起来更简单，看起来更优雅！不过值得注意的是，<code>Test.create()</code>方法的第三个入参，仍然是<code>ZhuZiCallback</code>这个接口类型，至于为什么可以用<code>Lambda</code>表达式代替，这一点放在后面再聊，下面重点说说<code>Lambda</code>表达式。</p>
<h2 id="泛型封装"><a href="#泛型封装" class="headerlink" title="泛型封装"></a>泛型封装</h2><blockquote>
<p>2025 年 7 月 29 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7402076531294470144">Java泛型大揭秘：从基础操作到实现原理，及如何规避常见陷阱与问题！作为每天跟代码打交道的我们，相信对泛型这个技术并不陌 - 掘金</a></p>
<p>基于泛型封装一个常用、通用的方法，即<code>Bean</code>拷贝场景，在日常编码设计中，都会将对象分为<code>BO、VO、DTO、DO、PO……</code>各种模型，为了满足不同业务，数据会在这些对象之间流转。</p>
<p>可是挨个属性<code>Get/Set</code>属实麻烦，在平时大家使用较多的就是<code>Spring</code>提供的<code>BeanUtils</code>这个工具类，但这个工具类用起来还是有点繁琐，比如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> userMapper.selectById(userId);<br><span class="hljs-type">UserVO</span> <span class="hljs-variable">userVO</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UserVO</span>();<br>BeanUtils.copyProperties(user, userVO);<br></code></pre></td></tr></table></figure>

<p>正如上述所示，每次都得手动<code>new</code>出目标对象才行，而且<code>BeanUtils</code>也没提供集合拷贝的方法，因此，我们就可以基于泛型封装两个通用方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-comment">/**</span><br><span class="hljs-comment"> * Bean拷贝工具类</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">BeanCopyUtil</span> &#123;<br>    <span class="hljs-comment">/*</span><br><span class="hljs-comment">    * 拷贝单个Bean对象</span><br><span class="hljs-comment">    * */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> &lt;T&gt; T <span class="hljs-title function_">copy</span><span class="hljs-params">(Object source, Class&lt;T&gt; clazz)</span> &#123;<br>        <span class="hljs-keyword">if</span> (<span class="hljs-literal">null</span> == source) &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>        &#125;<br>        T target;<br>        <span class="hljs-keyword">try</span> &#123;<br>            target = clazz.newInstance();<br>        &#125; <span class="hljs-keyword">catch</span> (InstantiationException | IllegalAccessException e) &#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;bean copy exception: &quot;</span> + e.getMessage());<br>        &#125;<br>        BeanUtils.copyProperties(source, target);<br>        <span class="hljs-keyword">return</span> target;<br>    &#125;<br><br>    <span class="hljs-comment">/*</span><br><span class="hljs-comment">    * 拷贝Bean对象集合</span><br><span class="hljs-comment">    * */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> &lt;T&gt; List&lt;T&gt; <span class="hljs-title function_">copyList</span><span class="hljs-params">(List&lt;T&gt; sourceList, Class&lt;T&gt; clazz)</span> &#123;<br>        <span class="hljs-keyword">if</span> (<span class="hljs-literal">null</span> == sourceList || <span class="hljs-number">0</span> == sourceList.size()) &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>        &#125;<br><br>        List&lt;T&gt; targetList = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;();<br>        <span class="hljs-keyword">for</span> (T source : sourceList) &#123;<br>            <span class="hljs-type">T</span> <span class="hljs-variable">target</span> <span class="hljs-operator">=</span> copy(source, clazz);<br>            targetList.add(target);<br>        &#125;<br>        <span class="hljs-keyword">return</span> targetList;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>基于这两个封装的方法，能特别方便的应对平时<code>Bean</code>拷贝场景，用起来也格外简单：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-comment">// 拷贝单个Bean对象</span><br><span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> userMapper.selectById(userId);<br><span class="hljs-type">UserVO</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> BeanCopyUtil.copy(user, UserVO.class);<br><br><span class="hljs-comment">// 拷贝Bean对象集合</span><br>List&lt;User&gt; users = userMapper.selectList();<br>List&lt;UserVO&gt; results = BeanCopyUtil.copyList(users, UserVO.class);<br></code></pre></td></tr></table></figure>

<p>谈谈 Lambda 表达式的语法：</p>
<p><code>Lambda</code>表达式，是<code>JDK1.8</code>从函数式编程语言中“借鉴”而来的特性，<code>Lambda</code>允许将一个函数作为方法的入参。而<code>Lambda</code>表达式的基础语法由三部分组成：</p>
<blockquote>
<p><code>()</code>包裹的参数列表、<code>–&gt;</code>符号、<code>&#123;&#125;</code>包裹的函数体。</p>
</blockquote>
<p>通过前面的例子来套入分析下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码Test.create(<span class="hljs-number">88888888</span>, <span class="hljs-string">&quot;竹子爱熊猫&quot;</span>, (ZhuZi zhuZi) -&gt; &#123;<br>    System.out.println(<span class="hljs-string">&quot;我是创建完竹子对象后的回调，创建的对象为：&quot;</span> + zhuZi);<br>&#125;);<br></code></pre></td></tr></table></figure>

<p><code>ZhuZi</code>代表是入参的类型，<code>zhuZi</code>代表是方法的参数名，这个名字你想叫啥就叫啥。<code>-&gt;</code>是<code>Lambda</code>表达式的固定语法，这个是固定的语法糖，不能改变成<code>→、_&gt;、=&gt;</code>或其他箭头。最后就是<code>&#123;&#125;</code>这对花括号包裹的代码块，实际上就是具体要执行的函数体，就跟方法体一样。</p>
<p>掌握上述基本语法后，下面再来看几类变种写法，先来看无参数的<code>lambda</code>写法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 无参数回调</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">NoArgsCallback</span> &#123;<br>    <span class="hljs-keyword">void</span> <span class="hljs-title function_">callback</span><span class="hljs-params">()</span>;<br>&#125;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Test</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">noArgs</span><span class="hljs-params">(NoArgsCallback noArgsCallback)</span> &#123;<br>        noArgsCallback.callback();<br>    &#125;<br>    <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>       Test.noArgs(() -&gt; &#123;<br>            System.out.println(<span class="hljs-string">&quot;我是无参数的lambda语法……&quot;</span>);<br>        &#125;);<br>    &#125;<br>&#125;<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment">* 执行结果：</span><br><span class="hljs-comment">*   我是无参数的lambda语法……</span><br><span class="hljs-comment">* */</span><br></code></pre></td></tr></table></figure>

<p>注意看上面无参数的<code>lambda</code>写法，和之前的唯一区别在于：<strong>如果对应的函数没有入参，那么参数列表部分就用<code>()</code>小括号代替即可</strong>。再来看看多参数：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 无参数回调</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">MultipleArgsCallback</span> &#123;<br>    <span class="hljs-keyword">void</span> <span class="hljs-title function_">callback</span><span class="hljs-params">(<span class="hljs-type">int</span> arg1, String arg2)</span>;<br>&#125;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Test</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">multipleArgs</span><span class="hljs-params">(<span class="hljs-type">int</span> arg1, String arg2, </span><br><span class="hljs-params">                            MultipleArgsCallback multipleArgsCallback)</span> &#123;<br>        multipleArgsCallback.callback(arg1, arg2);<br>    &#125;<br>    <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>       Test.multipleArgs(<span class="hljs-number">1</span>, <span class="hljs-string">&quot;竹子爱熊猫&quot;</span>, (<span class="hljs-type">int</span> a, String b) -&gt; &#123;<br>            System.out.println(<span class="hljs-string">&quot;我是&quot;</span> + b + <span class="hljs-string">&quot;，想要&quot;</span> + a + <span class="hljs-string">&quot;个点赞！&quot;</span>);<br>        &#125;);<br>    &#125;<br>&#125;<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment">* 执行结果：</span><br><span class="hljs-comment">*   我是竹子爱熊猫，想要1个点赞！</span><br><span class="hljs-comment">* */</span><br></code></pre></td></tr></table></figure>

<p>与无参数的写法对比，如果函数存在多个入参，只需要用<code>()</code>将参数列表包起来、多个参数用<code>,</code>逗号隔开就行，函数存在多少个入参，这里就需要定义多少个参数，顺序与函数定义的入参列表一一对应。好了，再回去看到只有一个入参的<code>lambda</code>案例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码Test.create(<span class="hljs-number">88888888</span>, <span class="hljs-string">&quot;竹子爱熊猫&quot;</span>, (ZhuZi zhuZi) -&gt; &#123;<br>    System.out.println(<span class="hljs-string">&quot;我是创建完竹子对象后的回调，创建的对象为：&quot;</span> + zhuZi);<br>&#125;);<br><br><span class="hljs-comment">// 可以优化为：</span><br>Test.create(<span class="hljs-number">88888888</span>, <span class="hljs-string">&quot;竹子爱熊猫&quot;</span>, zhuZi -&gt; <br>    System.out.println(<span class="hljs-string">&quot;我是创建完竹子对象后的回调，创建的对象为：&quot;</span> + zhuZi)<br>);<br></code></pre></td></tr></table></figure>

<p>区别在哪儿呢？优化之后的写法，参数列表没有<code>()</code>包裹了，函数体也没用<code>&#123;&#125;</code>包裹了，<code>sout</code>这行代码最后的<code>;</code>分号也去掉了，为啥可以这样写？因为这个案例中，参数只有一个，所以可以省略<code>()</code>；函数体也只有一行代码，所以<code>&#123;&#125;</code>也可以省略不写~</p>
<h2 id="并发"><a href="#并发" class="headerlink" title="并发"></a>并发</h2><blockquote>
<p>2025 年 4 月 1 日</p>
</blockquote>
<p>[01 使用了并发工具类库，线程安全就高枕无忧了吗？ (lianglianglee.com)](<a target="_blank" rel="noopener" href="https://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/Java">https://learn.lianglianglee.com/专栏/Java</a> 业务开发常见错误 100 例/01 使用了并发工具类库，线程安全就高枕无忧了吗？.md)</p>
<p>没有意识到线程重用导致用户信息错乱的 Bug。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> ThreadLocal&lt;Integer&gt; currentUser = ThreadLocal.withInitial(() -&gt; <span class="hljs-literal">null</span>);<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        System.out.println(<span class="hljs-string">&quot;Hello world!&quot;</span>);<br>        <span class="hljs-type">Map</span> <span class="hljs-variable">wrong</span> <span class="hljs-operator">=</span> wrong(<span class="hljs-number">1</span>);<br>        System.out.println(wrong);<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Map <span class="hljs-title function_">wrong</span><span class="hljs-params">(Integer integer)</span> &#123;<br>        <span class="hljs-comment">// 设置前先查询</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">before</span> <span class="hljs-operator">=</span> Thread.currentThread().getName() + <span class="hljs-string">&quot;:&quot;</span> + currentUser.get();<br>        <span class="hljs-comment">// 设置信息到 ThreadLocal</span><br>        currentUser.set(integer);<br>        <span class="hljs-comment">// 设置后再查询</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">after</span> <span class="hljs-operator">=</span> Thread.currentThread().getName() + <span class="hljs-string">&quot;:&quot;</span> + currentUser.get();<br>        System.out.println(before + <span class="hljs-string">&quot;-&gt;&quot;</span> + after);<br>        <span class="hljs-comment">// 汇总后返回</span><br>        HashMap&lt;Object, Object&gt; hashMap = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>        hashMap.put(<span class="hljs-string">&quot;before&quot;</span>, before);<br>        hashMap.put(<span class="hljs-string">&quot;after&quot;</span>, after);<br>        <span class="hljs-keyword">return</span> hashMap;<br>    &#125;<br></code></pre></td></tr></table></figure>

<p><strong>使用类似 ThreadLocal 工具来存放一些数据时，需要特别注意在代码运行完后，显式地去清空设置的数据</strong>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Map <span class="hljs-title function_">right</span><span class="hljs-params">(Integer integer)</span> &#123;<br>      <span class="hljs-comment">// 设置前先查询</span><br>      <span class="hljs-type">String</span> <span class="hljs-variable">before</span> <span class="hljs-operator">=</span> Thread.currentThread().getName() + <span class="hljs-string">&quot;:&quot;</span> + currentUser.get();<br>      <span class="hljs-comment">// 设置信息到 ThreadLocal</span><br>      currentUser.set(integer);<br>      <span class="hljs-keyword">try</span> &#123;<br>          <span class="hljs-comment">// 设置后再查询</span><br>          <span class="hljs-type">String</span> <span class="hljs-variable">after</span> <span class="hljs-operator">=</span> Thread.currentThread().getName() + <span class="hljs-string">&quot;:&quot;</span> + currentUser.get();<br>          System.out.println(before + <span class="hljs-string">&quot;-&gt;&quot;</span> + after);<br>          <span class="hljs-comment">// 汇总后返回</span><br>          HashMap&lt;Object, Object&gt; hashMap = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>          hashMap.put(<span class="hljs-string">&quot;before&quot;</span>, before);<br>          hashMap.put(<span class="hljs-string">&quot;after&quot;</span>, after);<br>          <span class="hljs-keyword">return</span> hashMap;<br>      &#125; <span class="hljs-keyword">finally</span> &#123;<br>          <span class="hljs-comment">// 最后移除 ThreadLocal 中的信息</span><br>          currentUser.remove();<br>      &#125;<br>  &#125;<br></code></pre></td></tr></table></figure>

<p>使用了线程安全的并发工具，并不代表解决了所有线程安全问题。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Main2</span> &#123;<br>    <span class="hljs-comment">// 线程个数</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> <span class="hljs-variable">THREAD_COUNT</span> <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;<br>    <span class="hljs-comment">// 总元素数量</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> <span class="hljs-variable">TOTAL_ELEMENTS</span> <span class="hljs-operator">=</span> <span class="hljs-number">1000</span>;<br><br>    <span class="hljs-comment">// 获取一个指定元素数量模拟数据的 ConcurrentHashMap</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> ConcurrentHashMap&lt;Integer, Integer&gt; <span class="hljs-title function_">getMockConcurrentHashMap</span><span class="hljs-params">(<span class="hljs-type">int</span> elements)</span> &#123;<br>        ConcurrentHashMap&lt;Integer, Integer&gt; concurrentHashMap = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ConcurrentHashMap</span>&lt;&gt;();<br>        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; elements; i++) &#123;<br>            concurrentHashMap.put(i, i);<br>        &#125;<br>        <span class="hljs-keyword">return</span> concurrentHashMap;<br>    &#125;<br><br>    <span class="hljs-comment">// 模拟数据</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> ConcurrentHashMap&lt;String, Long&gt; <span class="hljs-title function_">getData</span><span class="hljs-params">(<span class="hljs-type">int</span> count)</span> &#123;<br>        <span class="hljs-keyword">return</span> LongStream.rangeClosed(<span class="hljs-number">1</span>, count)<br>                .boxed()<br>                .collect(Collectors.toConcurrentMap(i -&gt; UUID.randomUUID().toString(), Function.identity(),<br>                        (o1, o2) -&gt; o1, ConcurrentHashMap::<span class="hljs-keyword">new</span>));<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException &#123;<br>        <span class="hljs-comment">// 初始900个元素</span><br>        ConcurrentHashMap&lt;String, Long&gt; concurrentHashMap = getData(TOTAL_ELEMENTS - <span class="hljs-number">100</span>);<br>        System.out.println(<span class="hljs-string">&quot;init size:&quot;</span> + concurrentHashMap.size());<br>        <span class="hljs-type">ForkJoinPool</span> <span class="hljs-variable">forkJoinPool</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ForkJoinPool</span>(THREAD_COUNT);<br>        <span class="hljs-comment">// 使用线程池并发处理逻辑</span><br>        forkJoinPool.execute(() -&gt; IntStream.rangeClosed(<span class="hljs-number">1</span>, <span class="hljs-number">10</span>).parallel().forEach(i -&gt; &#123;<br>            <span class="hljs-comment">// 查询还要补充多少元素</span><br>            <span class="hljs-type">int</span> <span class="hljs-variable">gap</span> <span class="hljs-operator">=</span> TOTAL_ELEMENTS - concurrentHashMap.size();<br>            System.out.println(<span class="hljs-string">&quot;gap size:&quot;</span> + gap);<br>            <span class="hljs-comment">// 补充元素</span><br>            concurrentHashMap.putAll(getData(gap));<br>        &#125;));<br><br>        <span class="hljs-comment">// 等待所有任务完成</span><br>        forkJoinPool.shutdown();<br>        forkJoinPool.awaitTermination(<span class="hljs-number">1</span>, TimeUnit.HOURS);<br>        <span class="hljs-comment">// 验证最后元素</span><br>        System.out.println(<span class="hljs-string">&quot;last size:&quot;</span> + concurrentHashMap.size());<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20250401222235818.png" alt="image-20250401222235818"></p>
<p>回到 ConcurrentHashMap，<strong>我们需要注意 ConcurrentHashMap 对外提供的方法或能力的限制：</strong></p>
<ul>
<li>使用了 ConcurrentHashMap，不代表对它的多个操作之间的状态是一致的，是没有其他线程在操作它的，如果需要确保需要手动加锁。</li>
<li>诸如 size、isEmpty 和 containsValue 等聚合方法，在并发情况下可能会反映 ConcurrentHashMap 的中间状态。因此在并发情况下，这些方法的返回值只能用作参考，而不能用于流程控制。显然，利用 size 方法计算差异值，是一个流程控制。</li>
<li>诸如 putAll 这样的聚合方法也不能确保原子性，在 putAll 的过程中去获取数据可能会获取到部分数据。</li>
</ul>
<p>代码的修改方案很简单，整段逻辑加锁即可：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 使用线程池并发处理逻辑</span><br>forkJoinPool.execute(() -&gt; IntStream.rangeClosed(<span class="hljs-number">1</span>, <span class="hljs-number">10</span>).parallel().forEach(i -&gt; &#123;<br>    <span class="hljs-keyword">synchronized</span> (concurrentHashMap) &#123;<br>        <span class="hljs-comment">// 查询还要补充多少元素</span><br>        <span class="hljs-type">int</span> <span class="hljs-variable">gap</span> <span class="hljs-operator">=</span> TOTAL_ELEMENTS - concurrentHashMap.size();<br>        System.out.println(<span class="hljs-string">&quot;gap size:&quot;</span> + gap);<br>        <span class="hljs-comment">// 补充元素</span><br>        concurrentHashMap.putAll(getData(gap));<br>    &#125;<br>&#125;));<br></code></pre></td></tr></table></figure>

<p>没有充分了解并发工具的特性，从而无法发挥其威力。</p>
<p>我们来看一个使用 Map 来统计 Key 出现次数的场景吧，这个逻辑在业务代码中非常常见。</p>
<figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs processing"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Main3</span> &#123;<br>    <span class="hljs-comment">// 循环次数</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> LOOP_COUNT = <span class="hljs-number">1000000</span>;<br>    <span class="hljs-comment">// 线程数量</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> THREAD_COUNT = <span class="hljs-number">10</span>;<br>    <span class="hljs-comment">// 元素数量</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> TOTAL_ELEMENTS = <span class="hljs-number">10</span>;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span>(<span class="hljs-built_in">String</span>[] args) &#123;<br>        Map&lt;<span class="hljs-built_in">String</span>, Long&gt; stringLongMap = <span class="hljs-title function_">normalUse</span>();<br>        System.<span class="hljs-property">out</span>.<span class="hljs-property">println</span>(stringLongMap);<br>    &#125;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Map&lt;<span class="hljs-built_in">String</span>, Long&gt; <span class="hljs-title function_">normalUse</span>() &#123;<br>        ConcurrentHashMap&lt;<span class="hljs-built_in">String</span>, Long&gt; concurrentHashMap = <span class="hljs-keyword">new </span><span class="hljs-class title_">ConcurrentHashMap</span>&lt;&gt;(TOTAL_ELEMENTS);<br>        System.<span class="hljs-property">out</span>.<span class="hljs-property">println</span>(<span class="hljs-string">&quot;init size:&quot;</span> + concurrentHashMap.<span class="hljs-property">size</span>());<br>        ForkJoinPool forkJoinPool = <span class="hljs-keyword">new </span><span class="hljs-class title_">ForkJoinPool</span>(THREAD_COUNT);<br>        forkJoinPool.<span class="hljs-property">execute</span>(() -&gt; IntStream.<span class="hljs-property">rangeClosed</span>(<span class="hljs-number">1</span>, LOOP_COUNT).<span class="hljs-property">parallel</span>().<span class="hljs-property">forEach</span>(i -&gt; &#123;<br>            <span class="hljs-comment">// 获得一个随机 key</span><br>            <span class="hljs-comment">// String key = UUID.randomUUID().toString().substring(0, 8);</span><br>            <span class="hljs-built_in">String</span> <span class="hljs-built_in">key</span> = <span class="hljs-string">&quot;item&quot;</span> + ThreadLocalRandom.<span class="hljs-property">current</span>().<span class="hljs-property">nextInt</span>(TOTAL_ELEMENTS);<br>            <span class="hljs-title function_">synchronized</span> (concurrentHashMap) &#123;<br>                <span class="hljs-comment">// 如果 Map 中已经有了这个 key，那么次数 +1</span><br>                <span class="hljs-keyword">if</span> (concurrentHashMap.<span class="hljs-property">containsKey</span>(<span class="hljs-built_in">key</span>)) &#123;<br>                    concurrentHashMap.<span class="hljs-property">put</span>(<span class="hljs-built_in">key</span>, (Long) concurrentHashMap.<span class="hljs-property">get</span>(<span class="hljs-built_in">key</span>) + <span class="hljs-number">1</span>);<br>                &#125;<br>                <span class="hljs-comment">// 如果 Map 中没有这个 key，那么放进去，次数初始化为 1</span><br>                <span class="hljs-keyword">else</span> &#123;<br>                    concurrentHashMap.<span class="hljs-property">put</span>(<span class="hljs-built_in">key</span>, <span class="hljs-number">1</span>L);<br>                &#125;<br>            &#125;<br>        &#125;));<br><br>        forkJoinPool.<span class="hljs-property">shutdown</span>();<br>        forkJoinPool.<span class="hljs-property">awaitQuiescence</span>(<span class="hljs-number">1</span>, TimeUnit.<span class="hljs-property">HOURS</span>);<br>        <span class="hljs-keyword">return</span> concurrentHashMap;<br>    &#125;<br>&#125;<br><br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20250401223807691.png" alt="image-20250401223807691"></p>
<p>这段代码在功能上没有问题，但无法充分发挥 ConcurrentHashMap 的威力，改进后的代码如下：</p>
<p>在这段改进后的代码中，我们巧妙利用了下面两点：</p>
<ul>
<li>使用 ConcurrentHashMap 的原子性方法 computeIfAbsent 来做复合逻辑操作，判断 Key 是否存在 Value，如果不存在则把 Lambda 表达式运行后的结果放入 Map 作为 Value，也就是新创建一个 LongAdder 对象，最后返回 Value。</li>
<li>由于 computeIfAbsent 方法返回的 Value 是 LongAdder，是一个线程安全的累加器，因此可以直接调用其 increment 方法进行累加。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Map&lt;String, Long&gt; <span class="hljs-title function_">goodUse</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException &#123;<br>      ConcurrentHashMap&lt;String, Long&gt; concurrentHashMap = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ConcurrentHashMap</span>&lt;&gt;(TOTAL_ELEMENTS);<br>      System.out.println(<span class="hljs-string">&quot;init size:&quot;</span> + concurrentHashMap.size());<br>      <span class="hljs-type">ForkJoinPool</span> <span class="hljs-variable">forkJoinPool</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ForkJoinPool</span>(THREAD_COUNT);<br>      forkJoinPool.execute(() -&gt; IntStream.rangeClosed(<span class="hljs-number">1</span>, LOOP_COUNT).parallel().forEach(i -&gt; &#123;<br>                  <span class="hljs-type">String</span> <span class="hljs-variable">key</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;item&quot;</span> + ThreadLocalRandom.current().nextInt(TOTAL_ELEMENTS);<br>                  <span class="hljs-comment">// 利用computeIfAbsent()方法来实例化LongAdder，然后利用LongAdder来进行线程安全计数</span><br>                  concurrentHashMap.computeIfAbsent(key, k -&gt; <span class="hljs-keyword">new</span> <span class="hljs-title class_">LongAdder</span>()).increment();<br>              &#125;<br>      ));<br>      forkJoinPool.shutdown();<br>      forkJoinPool.awaitTermination(<span class="hljs-number">1</span>, TimeUnit.HOURS);<br>      <span class="hljs-comment">//因为我们的Value是LongAdder而不是Long，所以需要做一次转换才能返回</span><br>      <span class="hljs-keyword">return</span> concurrentHashMap.entrySet().stream()<br>              .collect(Collectors.toMap(<br>                      e -&gt; e.getKey(),<br>                      e -&gt; e.getValue().longValue())<br>              );<br>  &#125;<br></code></pre></td></tr></table></figure>

<p>没有认清并发工具的使用场景，因而导致性能问题。</p>
<p>在 Java 中，CopyOnWriteArrayList 虽然是一个线程安全的 ArrayList，但因为其实现方式是，每次修改数据时都会复制一份数据出来，所以有明显的适用场景，即读多写少或者说希望无锁读的场景。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 测试并发写</span><br><span class="hljs-keyword">public</span> Map <span class="hljs-title function_">testWrite</span><span class="hljs-params">()</span> &#123;<br>    List&lt;Integer&gt; copyOnWriteArrayList = <span class="hljs-keyword">new</span> <span class="hljs-title class_">CopyOnWriteArrayList</span>&lt;&gt;();<br>    List&lt;Integer&gt; synchronizedList = Collections.synchronizedList(<span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;());<br>    <span class="hljs-type">StopWatch</span> <span class="hljs-variable">stopWatch</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StopWatch</span>();<br>    <span class="hljs-type">int</span> <span class="hljs-variable">loopCount</span> <span class="hljs-operator">=</span> <span class="hljs-number">100000</span>;<br>    stopWatch.start(<span class="hljs-string">&quot;Write:copyOnWriteArrayList&quot;</span>);<br>    <span class="hljs-comment">//循环100000次并发往CopyOnWriteArrayList写入随机元素</span><br>    IntStream.rangeClosed(<span class="hljs-number">1</span>, loopCount).parallel().forEach(__ -&gt; copyOnWriteArrayList.add(ThreadLocalRandom.current().nextInt(loopCount)));<br>    stopWatch.stop();<br>    stopWatch.start(<span class="hljs-string">&quot;Write:synchronizedList&quot;</span>);<br>    <span class="hljs-comment">//循环100000次并发往加锁的ArrayList写入随机元素</span><br>    IntStream.rangeClosed(<span class="hljs-number">1</span>, loopCount).parallel().forEach(__ -&gt; synchronizedList.add(ThreadLocalRandom.current().nextInt(loopCount)));<br>    stopWatch.stop();<br>    log.info(stopWatch.prettyPrint());<br>    <span class="hljs-type">Map</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>();<br>    result.put(<span class="hljs-string">&quot;copyOnWriteArrayList&quot;</span>, copyOnWriteArrayList.size());<br>    result.put(<span class="hljs-string">&quot;synchronizedList&quot;</span>, synchronizedList.size());<br>    <span class="hljs-keyword">return</span> result;<br>&#125;<br><span class="hljs-comment">//帮助方法用来填充List</span><br><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addAll</span><span class="hljs-params">(List&lt;Integer&gt; list)</span> &#123;<br>    list.addAll(IntStream.rangeClosed(<span class="hljs-number">1</span>, <span class="hljs-number">1000000</span>).boxed().collect(Collectors.toList()));<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 测试并发读</span><br><span class="hljs-keyword">public</span> Map <span class="hljs-title function_">testRead</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-comment">//创建两个测试对象</span><br>    List&lt;Integer&gt; copyOnWriteArrayList = <span class="hljs-keyword">new</span> <span class="hljs-title class_">CopyOnWriteArrayList</span>&lt;&gt;();<br>    List&lt;Integer&gt; synchronizedList = Collections.synchronizedList(<span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;());<br>    <span class="hljs-comment">//填充数据</span><br>    addAll(copyOnWriteArrayList);<br>    addAll(synchronizedList);<br>    <span class="hljs-type">StopWatch</span> <span class="hljs-variable">stopWatch</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StopWatch</span>();<br>    <span class="hljs-type">int</span> <span class="hljs-variable">loopCount</span> <span class="hljs-operator">=</span> <span class="hljs-number">1000000</span>;<br>    <span class="hljs-type">int</span> <span class="hljs-variable">count</span> <span class="hljs-operator">=</span> copyOnWriteArrayList.size();<br>    stopWatch.start(<span class="hljs-string">&quot;Read:copyOnWriteArrayList&quot;</span>);<br>    <span class="hljs-comment">//循环1000000次并发从CopyOnWriteArrayList随机查询元素</span><br>    IntStream.rangeClosed(<span class="hljs-number">1</span>, loopCount).parallel().forEach(__ -&gt; copyOnWriteArrayList.get(ThreadLocalRandom.current().nextInt(count)));<br>    stopWatch.stop();<br>    stopWatch.start(<span class="hljs-string">&quot;Read:synchronizedList&quot;</span>);<br>    <span class="hljs-comment">//循环1000000次并发从加锁的ArrayList随机查询元素</span><br>    IntStream.range(<span class="hljs-number">0</span>, loopCount).parallel().forEach(__ -&gt; synchronizedList.get(ThreadLocalRandom.current().nextInt(count)));<br>    stopWatch.stop();<br>    log.info(stopWatch.prettyPrint());<br>    <span class="hljs-type">Map</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>();<br>    result.put(<span class="hljs-string">&quot;copyOnWriteArrayList&quot;</span>, copyOnWriteArrayList.size());<br>    result.put(<span class="hljs-string">&quot;synchronizedList&quot;</span>, synchronizedList.size());<br>    <span class="hljs-keyword">return</span> result;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//帮助方法用来填充List</span><br><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addAll</span><span class="hljs-params">(List&lt;Integer&gt; list)</span> &#123;<br>    list.addAll(IntStream.rangeClosed(<span class="hljs-number">1</span>, <span class="hljs-number">1000000</span>).boxed().collect(Collectors.toList()));<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="新特性"><a href="#新特性" class="headerlink" title="新特性"></a>新特性</h2><blockquote>
<p>2024 年 6 月 20 日</p>
</blockquote>
<h3 id="JShell：快速验证简单的小问题"><a href="#JShell：快速验证简单的小问题" class="headerlink" title="JShell：快速验证简单的小问题"></a>JShell：快速验证简单的小问题</h3><p>挺有意思的小功能，可以用来做简单的输出和运算：</p>
<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20240620234548463.png" alt="image-20240620234548463"></p>
<h2 id="反射机制"><a href="#反射机制" class="headerlink" title="反射机制"></a>反射机制</h2><h3 id="什么是反射"><a href="#什么是反射" class="headerlink" title="什么是反射"></a>什么是反射</h3><blockquote>
<p>Java 中的<strong>反射机制</strong>是指：在运行状态中，对于任何一个类，我们都能获取到这个类的所有属性和方法，还可以调用这些属性和方法</p>
<h5 id="🔥-推荐阅读：Java-反射机制详解-JavaGuide-Java-面试-学习指南"><a href="#🔥-推荐阅读：Java-反射机制详解-JavaGuide-Java-面试-学习指南" class="headerlink" title="🔥 推荐阅读：Java 反射机制详解 | JavaGuide(Java 面试 + 学习指南)"></a>🔥 推荐阅读：<a target="_blank" rel="noopener" href="https://javaguide.cn/java/basis/reflection.html">Java 反射机制详解 | JavaGuide(Java 面试 + 学习指南)</a></h5></blockquote>
<h3 id="反射的应用场景"><a href="#反射的应用场景" class="headerlink" title="反射的应用场景"></a>反射的应用场景</h3><blockquote>
<p>Spring 中的依赖注入、RPC 调用、Java 中的动态代理实现</p>
</blockquote>
<h3 id="简单演示"><a href="#简单演示" class="headerlink" title="简单演示"></a>简单演示</h3><blockquote>
<p>👏 有关反射内容的介绍，网上多了去了，<strong>详尽且细致</strong>，可以参考<strong>本文开头的推荐阅读篇目</strong>挑选阅读</p>
</blockquote>
<ul>
<li>我们的特点就是<strong>不讲任何多余的废话</strong>，直接通过简单的代码演示，<strong>体会 Java 中的反射机制**</strong> <strong>（2023/10/24 午）</strong></li>
</ul>
<h4 id="构造-Person-对象"><a href="#构造-Person-对象" class="headerlink" title="构造 Person 对象"></a>构造 Person 对象</h4><ul>
<li><h5 id="成员属性"><a href="#成员属性" class="headerlink" title="成员属性"></a>成员属性</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 姓名</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">private</span> String name;<br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 分数</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">protected</span> Double grade;<br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 学生id</span><br><span class="hljs-comment"> */</span><br><span class="hljs-type">long</span> id;<br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 年龄</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-type">int</span> age;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="构造器"><a href="#构造器" class="headerlink" title="构造器"></a>构造器</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 无参构造器</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-title function_">Person</span><span class="hljs-params">()</span> &#123;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 带参构造器</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> name  姓名</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> grade 分数</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> id    学生id</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> age   年龄</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-title function_">Person</span><span class="hljs-params">(String name, Double grade, <span class="hljs-type">long</span> id, <span class="hljs-type">int</span> age)</span> &#123;<br>    <span class="hljs-built_in">this</span>.name = name;<br>    <span class="hljs-built_in">this</span>.grade = grade;<br>    <span class="hljs-built_in">this</span>.id = id;<br>    <span class="hljs-built_in">this</span>.age = age;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 带参构造器 私有</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> name  姓名</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> grade 分数</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> id    学生id</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">private</span> <span class="hljs-title function_">Person</span><span class="hljs-params">(String name, Double grade, <span class="hljs-type">long</span> id)</span> &#123;<br>    <span class="hljs-built_in">this</span>.name = name;<br>    <span class="hljs-built_in">this</span>.grade = grade;<br>    <span class="hljs-built_in">this</span>.id = id;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="成员方法"><a href="#成员方法" class="headerlink" title="成员方法"></a>成员方法</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">sayHello</span><span class="hljs-params">()</span>&#123;<br>    System.out.println(<span class="hljs-string">&quot;Hello!&quot;</span>);<br>&#125;<br><br><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">sayHi</span><span class="hljs-params">()</span>&#123;<br>    System.out.println(<span class="hljs-string">&quot;Hi!&quot;</span>);<br>&#125;<br><br><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">sayName</span><span class="hljs-params">(String name)</span>&#123;<br>    System.out.println(name);<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li>当然了，每个成员属性都提供有 <strong>getter、setter 方法</strong>，这里省略不写</li>
</ul>
<h4 id="获取-Class-对象"><a href="#获取-Class-对象" class="headerlink" title="获取 Class 对象"></a>获取 Class 对象</h4><ul>
<li><h5 id="代码详情："><a href="#代码详情：" class="headerlink" title="代码详情："></a>代码详情：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 1.1.通过具体的类的情况</span><br>Class&lt;Person&gt; personClass1 = Person.class;<br><span class="hljs-comment">// 1.2.通过现有的对象实例</span><br><span class="hljs-type">Person</span> <span class="hljs-variable">person</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>();<br>Class&lt;? <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Person</span>&gt; personClass2 = person.getClass();<br><span class="hljs-comment">// 1.3.通过读取类路径</span><br>Class&lt;?&gt; personClass3 = Class.forName(<span class="hljs-string">&quot;反射.Person&quot;</span>);<br><br>System.out.println(<span class="hljs-string">&quot;-----------获取Class对象-----------&quot;</span>);<br>System.out.println(personClass1);<br>System.out.println(personClass2);<br>System.out.println(personClass3);<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="执行结果如下："><a href="#执行结果如下：" class="headerlink" title="执行结果如下："></a>执行结果如下：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231024154344266.png" alt="image-20231024154344266"></p>
<h4 id="获取属性"><a href="#获取属性" class="headerlink" title="获取属性"></a>获取属性</h4><ul>
<li><h5 id="代码详情：-1"><a href="#代码详情：-1" class="headerlink" title="代码详情："></a>代码详情：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 2.1.获取属性(非私有)</span><br>Field[] fields = personClass1.getFields();<br>System.out.println(<span class="hljs-string">&quot;-----------获取属性(Public)-----------&quot;</span>);<br><span class="hljs-keyword">for</span> (Field field : fields) &#123;<br>    System.out.println(<span class="hljs-string">&quot;属性名: &quot;</span> + field.getName() + <span class="hljs-string">&quot; | 类型: &quot;</span> + field.getType());<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 2.2.获取属性(所有)</span><br><span class="hljs-type">Person</span> <span class="hljs-variable">person1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-string">&quot;邓哈哈&quot;</span>, <span class="hljs-number">99.0</span>, <span class="hljs-number">18889898988L</span>, <span class="hljs-number">18</span>);<br><br>Field[] fields2 = personClass1.getDeclaredFields();<br>System.out.println(<span class="hljs-string">&quot;-----------获取属性(所有)-----------&quot;</span>);<br><span class="hljs-keyword">for</span> (Field field : fields2) &#123;<br>    field.setAccessible(<span class="hljs-literal">true</span>); <span class="hljs-comment">// 启用访问私有字段</span><br><br>    System.out.println(<span class="hljs-string">&quot;属性名: &quot;</span> + field.getName()<br>            + <span class="hljs-string">&quot; | 类型: &quot;</span> + field.getType()<br>            + <span class="hljs-string">&quot; | 访问修饰符: &quot;</span> + field.getModifiers()<br>            + <span class="hljs-string">&quot; | 属性值: &quot;</span> + field.get(person1));<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="执行结果如下：-（2023-10-24-午）"><a href="#执行结果如下：-（2023-10-24-午）" class="headerlink" title="执行结果如下： （2023/10/24 午）"></a>执行结果如下： <strong>（2023/10/24 午）</strong></h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231024154829028.png" alt="image-20231024154829028"></p>
<h4 id="获取方法"><a href="#获取方法" class="headerlink" title="获取方法"></a>获取方法</h4><ul>
<li><h5 id="代码详情：-2"><a href="#代码详情：-2" class="headerlink" title="代码详情："></a>代码详情：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 3.1.获取方法(所有)</span><br>System.out.println(<span class="hljs-string">&quot;-----------获取方法(所有)-----------&quot;</span>);<br>Method[] methods = personClass1.getMethods();<br><span class="hljs-keyword">for</span> (Method method : methods) &#123;<br>    System.out.println(<span class="hljs-string">&quot;方法名: &quot;</span> + method.getName());<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 3.2.调用指定方法(指定方法名)</span><br>System.out.println(<span class="hljs-string">&quot;-----------调用指定方法(指定方法名)-----------&quot;</span>);<br><span class="hljs-type">Method</span> <span class="hljs-variable">sayHello</span> <span class="hljs-operator">=</span> personClass1.getMethod(<span class="hljs-string">&quot;sayHello&quot;</span>);<br>System.out.print(<span class="hljs-string">&quot;方法名: &quot;</span> + sayHello.getName() + <span class="hljs-string">&quot; | 调用结果: &quot;</span>);<br>sayHello.invoke(person);<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 3.3.调用私有方法</span><br>System.out.println(<span class="hljs-string">&quot;-----------调用私有方法-----------&quot;</span>);<br><span class="hljs-type">Method</span> <span class="hljs-variable">sayHi</span> <span class="hljs-operator">=</span> personClass1.getDeclaredMethod(<span class="hljs-string">&quot;sayHi&quot;</span>);<br>sayHi.setAccessible(<span class="hljs-literal">true</span>);<br>System.out.print(<span class="hljs-string">&quot;方法名: &quot;</span> + sayHi.getName() + <span class="hljs-string">&quot; | 调用结果: &quot;</span>);<br>sayHi.invoke(person);<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 3.4.调用带参方法</span><br>System.out.println(<span class="hljs-string">&quot;-----------调用带参方法-----------&quot;</span>);<br><span class="hljs-type">Method</span> <span class="hljs-variable">sayName</span> <span class="hljs-operator">=</span> personClass1.getDeclaredMethod(<span class="hljs-string">&quot;sayName&quot;</span>, String.class);<br>sayName.setAccessible(<span class="hljs-literal">true</span>);<br>System.out.print(<span class="hljs-string">&quot;方法名: &quot;</span> + sayName.getName() + <span class="hljs-string">&quot; | 调用结果: &quot;</span>);<br>sayName.invoke(person, <span class="hljs-string">&quot;邓哈哈&quot;</span>);<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="执行结果如下：-1"><a href="#执行结果如下：-1" class="headerlink" title="执行结果如下："></a>执行结果如下：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231024154741899.png" alt="image-20231024154741899"></p>
<h4 id="获取构造器"><a href="#获取构造器" class="headerlink" title="获取构造器"></a>获取构造器</h4><ul>
<li><h5 id="代码详情：-3"><a href="#代码详情：-3" class="headerlink" title="代码详情："></a>代码详情：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 4.获取构造器</span><br><span class="hljs-comment">// 4.1.获取构造器(Public)</span><br>System.out.println(<span class="hljs-string">&quot;-----------获取构造器(Public)-----------&quot;</span>);<br>Constructor&lt;?&gt;[] constructors = personClass1.getConstructors();<br><span class="hljs-keyword">for</span> (Constructor constructor1 : constructors) &#123;<br>    System.out.println(constructor1);<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 4.2.获取构造器(所有)</span><br>System.out.println(<span class="hljs-string">&quot;-----------获取构造器(所有)-----------&quot;</span>);<br>Constructor[] constructors01 = personClass1.getDeclaredConstructors();<br><span class="hljs-keyword">for</span> (Constructor constructor1 : constructors01) &#123;<br>    System.out.println(constructor1);<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="执行结果如下：-（2023-10-24-午）-1"><a href="#执行结果如下：-（2023-10-24-午）-1" class="headerlink" title="执行结果如下： （2023/10/24 午）"></a>执行结果如下： <strong>（2023/10/24 午）</strong></h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231024155438415.png" alt="image-20231024155438415"></p>
<h3 id="踩坑记录"><a href="#踩坑记录" class="headerlink" title="踩坑记录"></a>踩坑记录</h3><p><img src="http://blog.memory-life.xyz/image-20231023223351284.png" alt="image-20231023223351284"></p>
<h2 id="代理模式"><a href="#代理模式" class="headerlink" title="代理模式"></a>代理模式</h2><h3 id="什么是代理"><a href="#什么是代理" class="headerlink" title="什么是代理"></a>什么是代理</h3><blockquote>
<p>Java 中的代理模式是指：使用代理对象来代替对真实对象的访问，这样可以在不修改原目标对象的前提下，提供额外的功能操作</p>
<ul>
<li>代理模式可<strong>隐藏客户端真正调用的对象</strong>，<strong>实现代码解耦</strong>，增强系统的可维护性和可扩展性</li>
<li>代理模式常用于需要<strong>控制对对象的访问，并提供远程访问、安全检查和缓存</strong>等功能 <strong>（2023/10/20 午）</strong></li>
</ul>
<h5 id="🔥-推荐阅读：Java-代理模式详解-JavaGuide-Java-面试-学习指南"><a href="#🔥-推荐阅读：Java-代理模式详解-JavaGuide-Java-面试-学习指南" class="headerlink" title="🔥 推荐阅读：Java 代理模式详解 | JavaGuide(Java 面试 + 学习指南)"></a>🔥 推荐阅读：<a target="_blank" rel="noopener" href="https://javaguide.cn/java/basis/proxy.html">Java 代理模式详解 | JavaGuide(Java 面试 + 学习指南)</a></h5></blockquote>
<h3 id="静态代理"><a href="#静态代理" class="headerlink" title="静态代理"></a>静态代理</h3><h4 id="实现步骤"><a href="#实现步骤" class="headerlink" title="实现步骤"></a>实现步骤</h4><ul>
<li>定义一个接口及其实现类（被代理类）</li>
<li>创建一个代理类，同样实现这个接口</li>
<li>将目标对象注入进代理类，在代理类的对应方法中调用目标类的对应方法，这样我们就可以通过代理类屏蔽对目标对象的访问，并且可以在目标方法执行前后做一些自己想做的事情</li>
</ul>
<h4 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h4><ul>
<li><h5 id="短信发送接口（2023-10-24-午）"><a href="#短信发送接口（2023-10-24-午）" class="headerlink" title="短信发送接口（2023/10/24 午）"></a>短信发送接口（2023/10/24 午）</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 短信发送接口</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">SmsService</span> &#123;<br>    <span class="hljs-keyword">void</span> <span class="hljs-title function_">send</span><span class="hljs-params">(String message)</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="短信发送实现类"><a href="#短信发送实现类" class="headerlink" title="短信发送实现类"></a>短信发送实现类</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 短信发送实现</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SmsServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">SmsService</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">send</span><span class="hljs-params">(String message)</span> &#123;<br>        System.out.println(<span class="hljs-string">&quot;send message:&quot;</span> + message);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="短信发送代理类"><a href="#短信发送代理类" class="headerlink" title="短信发送代理类"></a>短信发送代理类</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 短信发送代理类</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SmsProxy</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">SmsService</span> &#123;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> SmsService smsService;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">SmsProxy</span><span class="hljs-params">(SmsService smsService)</span> &#123;<br>        <span class="hljs-built_in">this</span>.smsService = smsService;<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">send</span><span class="hljs-params">(String message)</span> &#123;<br>        <span class="hljs-comment">// 调用方法之前，我们可以添加自己的操作</span><br>        System.out.println(<span class="hljs-string">&quot;----------在send()方法之前执行的操作----------&quot;</span>);<br>        smsService.send(message);<br>        <span class="hljs-comment">// 调用方法之后，我们同样可以添加自己的操作</span><br>        System.out.println(<span class="hljs-string">&quot;----------在send()方法之后执行的操作----------&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="实际使用"><a href="#实际使用" class="headerlink" title="实际使用"></a>实际使用</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Main</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        <span class="hljs-type">SmsService</span> <span class="hljs-variable">smsService</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">SmsServiceImpl</span>();<br>        <span class="hljs-type">SmsProxy</span> <span class="hljs-variable">smsProxy</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">SmsProxy</span>(smsService);<br>        smsProxy.send(<span class="hljs-string">&quot;java&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="执行结果如下：-2"><a href="#执行结果如下：-2" class="headerlink" title="执行结果如下："></a>执行结果如下：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231024163414514.png" alt="image-20231024163414514"></p>
<h3 id="动态代理"><a href="#动态代理" class="headerlink" title="动态代理"></a>动态代理</h3><blockquote>
<p>相较于静态代理来说，动态代理要更加灵活，我们不需要针对每个目标类都单独创建一个代理类，也不必需要实现接口，我们可以直接代理实现类</p>
<p>👏 <strong>实现方式</strong>：就 Java 来说，动态代理的实现方式有很多种，比如 <strong>JDK 动态代理</strong>、<strong>CGLIB 动态代理</strong></p>
<p>动态代理在我们日常开发中几乎用不到，或者说使用相对较少，但在框架中，几乎是必用的一门技术。学会了动态代理之后，对于我们理解和学习各种框架的原理也非常有帮助</p>
<p>🧯 <strong>使用场景</strong>：<strong>Spring AOP</strong>、<strong>RPC 框架</strong></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/m0_61761695/article/details/134633078">7000 字详解 动态代理(JDK 动态代理 CGLIB 动态代理)与静态代理_jdk17 代理-CSDN 博客</a></p>
</blockquote>
<h4 id="JDK-动态代理"><a href="#JDK-动态代理" class="headerlink" title="JDK 动态代理"></a>JDK 动态代理</h4><p>JDK 动态代理的实现原理是：<strong>动态创建代理类，然后通过指定类加载器进行加载</strong>。在创建代理对象时，需要将 InvocationHandler 对象作为构造参数传入；当调用代理对象时，会调用 InvocationHandler.invoke() 方法，从而执行代理逻辑，最终调用真正业务对象的相应方法。</p>
<blockquote>
<p><strong>JDK 动态代理执行流程</strong>：通过 <code>Proxy</code> 类的 <code>newProxyInstance()</code> 创建的代理对象再调用方法的时候，实际会调用代理类（实现 <code>InvocationHandler</code> 接口的类）的 <code>invoke()</code> 方法。</p>
<p>还是那句话，有关 JDK 动态代理的实现原理，这里不做详尽的解释，我们主打不讲任何多余的废话，通过简单的代码演示：</p>
<p>🔥 <strong>推荐阅读</strong>：<a target="_blank" rel="noopener" href="https://javaguide.cn/java/basis/proxy.html">Java 代理模式详解 | JavaGuide(Java 面试 + 学习指南)</a></p>
</blockquote>
<ul>
<li><h5 id="定义发送短信的接口（2023-10-24-晚）"><a href="#定义发送短信的接口（2023-10-24-晚）" class="headerlink" title="定义发送短信的接口（2023/10/24 晚）"></a>定义发送短信的接口（2023/10/24 晚）</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 短信发送接口</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">SmsService</span> &#123;<br>    String <span class="hljs-title function_">send</span><span class="hljs-params">(String message)</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="实现发送短信的接口"><a href="#实现发送短信的接口" class="headerlink" title="实现发送短信的接口"></a>实现发送短信的接口</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 实现短信发送接口</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SmsServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">SmsService</span> &#123;<br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">send</span><span class="hljs-params">(String message)</span> &#123;<br>        System.out.println(<span class="hljs-string">&quot;send message:&quot;</span> + message);<br>        <span class="hljs-keyword">return</span> message;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="定义-JDK-动态代理类"><a href="#定义-JDK-动态代理类" class="headerlink" title="定义 JDK 动态代理类"></a>定义 JDK 动态代理类</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 定义 JDK 动态代理类</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DebugInvocationHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">InvocationHandler</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 代理类中的真实对象</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Object target;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">DebugInvocationHandler</span><span class="hljs-params">(Object target)</span> &#123;<br>        <span class="hljs-built_in">this</span>.target = target;<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> Object <span class="hljs-title function_">invoke</span><span class="hljs-params">(Object proxy, Method method, Object[] args)</span> <span class="hljs-keyword">throws</span> InvocationTargetException, IllegalAccessException &#123;<br>        <span class="hljs-comment">// 调用方法之前，我们可以添加自己的操作</span><br>        System.out.println(<span class="hljs-string">&quot;----------在send()方法之前执行的操作----------&quot;</span>);<br>        <span class="hljs-type">Object</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> method.invoke(target, args);<br>        <span class="hljs-comment">// 调用方法之后，我们同样可以添加自己的操作</span><br>        System.out.println(<span class="hljs-string">&quot;----------在send()方法之后执行的操作----------&quot;</span>);<br>        <span class="hljs-keyword">return</span> result;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="获取代理对象的工厂类"><a href="#获取代理对象的工厂类" class="headerlink" title="获取代理对象的工厂类"></a>获取代理对象的工厂类</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 获取代理对象的工厂类</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JdkProxyFactory</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Object <span class="hljs-title function_">getProxy</span><span class="hljs-params">(Object target)</span> &#123;<br>        <span class="hljs-keyword">return</span> Proxy.newProxyInstance(<br>                target.getClass().getClassLoader(), <span class="hljs-comment">// 目标类的类加载器</span><br>                target.getClass().getInterfaces(),  <span class="hljs-comment">// 代理需要实现的接口，可指定多个</span><br>                <span class="hljs-keyword">new</span> <span class="hljs-title class_">DebugInvocationHandler</span>(target)   <span class="hljs-comment">// 代理对象对应的自定义 InvocationHandler</span><br>        );<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="实际使用-1"><a href="#实际使用-1" class="headerlink" title="实际使用"></a>实际使用</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Main</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        <span class="hljs-type">SmsService</span> <span class="hljs-variable">smsService</span> <span class="hljs-operator">=</span> (SmsService) JdkProxyFactory.getProxy(<span class="hljs-keyword">new</span> <span class="hljs-title class_">SmsServiceImpl</span>());<br>        smsService.send(<span class="hljs-string">&quot;java&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="执行结果如下：-3"><a href="#执行结果如下：-3" class="headerlink" title="执行结果如下："></a>执行结果如下：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231024223700470.png" alt="image-20231024223700470"></p>
<h4 id="CGLIB-动态代理"><a href="#CGLIB-动态代理" class="headerlink" title="CGLIB 动态代理"></a>CGLIB 动态代理</h4><blockquote>
<p><strong>CGLIB 动态代理执行流程</strong>：自定义 <code>MethodInterceptor</code> 并重写 <code>intercept</code> 方法，<code>intercept</code> 用于拦截增强被代理增强的方法，通过 <code>Enhancer</code> 类的 <code>create()</code> 创建代理类。当代理类调用方法的时候，实际调用的是 <code>MethodIntercept</code> 中的 <code>intercept</code> 方法</p>
<p>有关 CGLIB 动态代理的实现原理，这里不做详尽的解释，我们主打不讲任何多余的废话，通过简单的代码演示：</p>
<p>🔥 <strong>推荐阅读</strong>：<a target="_blank" rel="noopener" href="https://javaguide.cn/java/basis/proxy.html">Java 代理模式详解 | JavaGuide(Java 面试 + 学习指南)</a><strong>（2023/10/30 晚）</strong></p>
</blockquote>
<h2 id="I-O-流"><a href="#I-O-流" class="headerlink" title="I/O 流"></a>I/O 流</h2><h3 id="字节流"><a href="#字节流" class="headerlink" title="字节流"></a>字节流</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testFileInputStream</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-keyword">try</span> &#123;<br>        <span class="hljs-type">FileInputStream</span> <span class="hljs-variable">fis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;D:\\Project\\IDEA\\demo\\demo2\\demo\\algorisem\\IO\\IO流练习\\text\\hello.txt&quot;</span>);<br><br>        <span class="hljs-type">int</span> content;<br>        <span class="hljs-keyword">while</span> ((content = fis.read()) != -<span class="hljs-number">1</span>) &#123;<br>            System.out.println(content);<br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testFileInputStream2</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-keyword">try</span> &#123;<br>        <span class="hljs-type">FileInputStream</span> <span class="hljs-variable">fis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;D:\\Project\\IDEA\\demo\\demo2\\demo\\algorisem\\IO\\IO流练习\\text\\hello.txt&quot;</span>);<br><br>        <span class="hljs-type">int</span> len;<br>        <span class="hljs-type">byte</span>[] bytes = <span class="hljs-keyword">new</span> <span class="hljs-title class_">byte</span>[<span class="hljs-number">1024</span>];<br>        <span class="hljs-keyword">while</span> ((len = fis.read(bytes)) != -<span class="hljs-number">1</span>) &#123;<br>            System.out.print(<span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(bytes, <span class="hljs-number">0</span>, len));<br>            System.out.print(len);<br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testFileReader</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-keyword">try</span> &#123;<br>        <span class="hljs-type">FileReader</span> <span class="hljs-variable">fr</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileReader</span>(<span class="hljs-string">&quot;D:\\Project\\IDEA\\demo\\demo2\\demo\\algorisem\\IO\\IO流练习\\text\\hello.txt&quot;</span>);<br><br>        <span class="hljs-type">int</span> content;<br>        <span class="hljs-keyword">while</span> ((content = fr.read()) != -<span class="hljs-number">1</span>) &#123;<br>            System.out.println((<span class="hljs-type">char</span>) content);<br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testFileReader2</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-keyword">try</span> &#123;<br>        <span class="hljs-type">FileReader</span> <span class="hljs-variable">fr</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileReader</span>(<span class="hljs-string">&quot;D:\\Project\\IDEA\\demo\\demo2\\demo\\algorisem\\IO\\IO流练习\\text\\hello.txt&quot;</span>);<br><br>        <span class="hljs-type">int</span> len;<br>        <span class="hljs-type">char</span>[] chars = <span class="hljs-keyword">new</span> <span class="hljs-title class_">char</span>[<span class="hljs-number">1024</span>];<br>        <span class="hljs-keyword">while</span> ((len = fr.read(chars)) != -<span class="hljs-number">1</span>) &#123;<br>            System.out.println(<span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(chars, <span class="hljs-number">0</span>, len));<br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testBufferInputStream</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-keyword">try</span> &#123;<br>        <span class="hljs-type">BufferedInputStream</span> <span class="hljs-variable">bis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedInputStream</span>(<span class="hljs-keyword">new</span><br>                <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;D:\\Project\\IDEA\\demo\\demo2\\demo\\algorisem\\IO\\IO流练习\\text\\hello.txt&quot;</span>));<br><br>        <span class="hljs-type">String</span> <span class="hljs-variable">str</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(bis.readAllBytes());<br>        System.out.println(str);<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">copy_pdf_to_another_pdf_with_byte_array_buffer_stream</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-comment">// 记录开始时间</span><br>    <span class="hljs-type">long</span> <span class="hljs-variable">start</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>    <span class="hljs-keyword">try</span> (<span class="hljs-type">BufferedInputStream</span> <span class="hljs-variable">bis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedInputStream</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;D:\\Project\\IDEA\\demo\\demo2\\demo\\algorisem\\IO\\IO流练习\\text\\1.png&quot;</span>));<br>         <span class="hljs-type">BufferedOutputStream</span> <span class="hljs-variable">bos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedOutputStream</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;D:\\Project\\IDEA\\demo\\demo2\\demo\\algorisem\\IO\\IO流练习\\text\\111.png&quot;</span>))) &#123;<br>        <span class="hljs-type">int</span> len;<br>        <span class="hljs-type">byte</span>[] bytes = <span class="hljs-keyword">new</span> <span class="hljs-title class_">byte</span>[<span class="hljs-number">4</span> * <span class="hljs-number">1024</span>];<br>        <span class="hljs-keyword">while</span> ((len = bis.read(bytes)) != -<span class="hljs-number">1</span>) &#123;<br>            bos.write(bytes, <span class="hljs-number">0</span>, len);<br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        e.printStackTrace();<br>    &#125;<br>    <span class="hljs-comment">// 记录结束时间</span><br>    <span class="hljs-type">long</span> <span class="hljs-variable">end</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>    System.out.println(<span class="hljs-string">&quot;使用缓冲流复制PDF文件总耗时:&quot;</span> + (end - start) + <span class="hljs-string">&quot; 毫秒&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testFileOutputStream</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-keyword">try</span> &#123;<br>        <span class="hljs-type">FileOutputStream</span> <span class="hljs-variable">fos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;D:\\Project\\IDEA\\demo\\demo2\\demo\\algorisem\\IO\\IO流练习\\text\\hello2.txt&quot;</span>);<br><br>        <span class="hljs-type">byte</span>[] bytes = <span class="hljs-string">&quot;你好，这又是一段话&quot;</span>.getBytes();<br>        fos.write(bytes);<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">copyInputStream</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-keyword">try</span> &#123;<br>        <span class="hljs-type">FileInputStream</span> <span class="hljs-variable">fis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;D:\\Project\\IDEA\\demo\\demo2\\demo\\algorisem\\IO\\IO流练习\\text\\hello2.txt&quot;</span>);<br>        <span class="hljs-type">FileOutputStream</span> <span class="hljs-variable">fos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;D:\\Project\\IDEA\\demo\\demo2\\demo\\algorisem\\IO\\IO流练习\\text\\hello3.txt&quot;</span>);<br><br>        <span class="hljs-type">int</span> len;<br>        <span class="hljs-type">byte</span>[] bytes = <span class="hljs-keyword">new</span> <span class="hljs-title class_">byte</span>[<span class="hljs-number">1024</span>];<br>        <span class="hljs-keyword">while</span> ((len = fis.read(bytes)) != -<span class="hljs-number">1</span>) &#123;<br>            fos.write(bytes, <span class="hljs-number">0</span>, len);<br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="字符流"><a href="#字符流" class="headerlink" title="字符流"></a>字符流</h3><h2 id="注解"><a href="#注解" class="headerlink" title="注解"></a>注解</h2><blockquote>
<p>🍖 推荐阅读：<a target="_blank" rel="noopener" href="https://juejin.cn/post/7172580526589476878">Spring Boot 自定义注解 - 掘金 (juejin.cn)</a><strong>（2024/01/07 晚）</strong></p>
</blockquote>
<h3 id="元注解"><a href="#元注解" class="headerlink" title="元注解"></a>元注解</h3><h4 id="Target"><a href="#Target" class="headerlink" title="@Target"></a>@Target</h4><ul>
<li>注解作用的范围</li>
</ul>
<h4 id="Retention"><a href="#Retention" class="headerlink" title="@Retention"></a>@Retention</h4><ul>
<li>注解的生命周期</li>
</ul>
<h4 id="Document"><a href="#Document" class="headerlink" title="@Document"></a>@Document</h4><ul>
<li>注解生成在 Javadoc 生成的文档中</li>
</ul>
<h4 id="Inherited"><a href="#Inherited" class="headerlink" title="@Inherited"></a>@Inherited</h4><ul>
<li>注解可被子类继承</li>
</ul>
<h3 id="JDK-内置注解"><a href="#JDK-内置注解" class="headerlink" title="JDK 内置注解"></a>JDK 内置注解</h3><h4 id="Override"><a href="#Override" class="headerlink" title="@Override"></a>@Override</h4><h4 id="Deperecated"><a href="#Deperecated" class="headerlink" title="@Deperecated"></a>@Deperecated</h4><h4 id="SuppressWarnings"><a href="#SuppressWarnings" class="headerlink" title="@SuppressWarnings"></a>@SuppressWarnings</h4><h3 id="自定义注解"><a href="#自定义注解" class="headerlink" title="自定义注解"></a>自定义注解</h3><h2 id="八股吟唱"><a href="#八股吟唱" class="headerlink" title="八股吟唱"></a>八股吟唱</h2><h3 id="谈一谈你对多态的理解"><a href="#谈一谈你对多态的理解" class="headerlink" title="谈一谈你对多态的理解"></a>谈一谈你对多态的理解</h3><p>我们经常讲，面向对象有三大特征：封装、继承和多态（2023/11/18 早）</p>
<p>多态是面向对象编程中相当重要的一个概念，它允许通过父类类型的引用变量引用子类对象，在运行时根据实际的对象类型来确定调用哪个方法，一个对象能够根据不同的场景表现出多种形态</p>
<p>那多态具体是怎么实现的？在编译时，Java 编译器只能知道变量的声明类型，也就是父类类型，而无法确定实际的对象类型；而在运行时，Java 虚拟机会通过动态绑定解析出实际对象的类型，根据实际的对象类型调用被子类重写的方法。</p>
<p>也就是说，编译器会把方法的绑定，即方法的具体调用推迟到运行时，这就是动态绑定，<strong>这就是多态的实现原理</strong></p>
<p>我们发现使用多态有这样的好处：我们通过父类类型的引用来访问子类对象的方法，统一对象的接口，这是接口统一性；子类对象可以随时替代父类对象，向上转型，这就是可替换性；我们可以通过添加新的子类，扩展系统功能，这就是可扩展性；通过多态，能够实现对象间的解耦，因为我们不再需要指定具体对象去实现具体方法了，这使得代码更加简洁通用、更加易于维护</p>
<p>我们在编码开发中接触到的方法重载、方法重写、接口实现就是多态的具体实现，方法重载体现的是编译时的多态，而方法重写和接口实现体现的是运行时的多态</p>
<h3 id="String-内容不可变"><a href="#String-内容不可变" class="headerlink" title="String 内容不可变"></a>String 内容不可变</h3><blockquote>
<p>🔥 有关 Java 中 String 内容不可变的解释<strong>（2023/11/19 午）</strong></p>
</blockquote>
<p>我们经常听到这样的定义：String 对象一旦被创建，其内容就一定不可变</p>
<p>这句话的意思是：对该对象的所有操作（如 <code>replace()</code>、<code>contact()</code>、<code>substring()</code>）都将返回新的 String 对象，而不是在原 String 对象的内容上作修改。这些操作也都是不被允许的：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">String</span> <span class="hljs-variable">s</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;Hello&quot;</span>;<br><br>s.charAt(<span class="hljs-number">0</span>) = <span class="hljs-string">&#x27;h&#x27;</span>; <span class="hljs-comment">// 编译错误：Cannot assign to &#x27;char&#x27; field in an array of &#x27;final&#x27; value</span><br><br>s.length() = <span class="hljs-number">5</span>; <span class="hljs-comment">// 编译错误：Cannot assign to &#x27;int&#x27; field in an array of &#x27;final&#x27; value</span><br><br><span class="hljs-type">String</span> <span class="hljs-variable">t</span> <span class="hljs-operator">=</span> s.substring(<span class="hljs-number">0</span>, <span class="hljs-number">5</span>); <span class="hljs-comment">// 可以，但结果是新的 String 对象，不是对原有对象的修改</span><br><br>t = t + <span class="hljs-string">&quot; World&quot;</span>; <span class="hljs-comment">// 可以，但结果是新的 String 对象，不是对原有对象的修改</span><br></code></pre></td></tr></table></figure>

<p>为什么是这样的？String 对象内容不可变是如何保证的，这样做又有什么好处呢？</p>
<p>String 类被设计出来是为了方便我们对字符串进行操作，我们常见的字符串拼接、比较字符串内容、字符串长度等等，应用十分广泛。而 String 类底层是通过 char [] 数组（Java 9 之后改为 byte [] 实现了）来维护字符串：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">String</span><br>    <span class="hljs-keyword">implements</span> <span class="hljs-title class_">java</span>.io.Serializable, Comparable&lt;String&gt;, CharSequence &#123;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * The value is used for character storage.</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@implNote</span> This field is trusted by the VM, and is a subject to</span><br><span class="hljs-comment">     * constant folding if String instance is constant. Overwriting this</span><br><span class="hljs-comment">     * field after construction will cause problems.</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * Additionally, it is marked with &#123;<span class="hljs-doctag">@link</span> Stable&#125; to trust the contents</span><br><span class="hljs-comment">     * of the array. No other facility in JDK provides this functionality (yet).</span><br><span class="hljs-comment">     * &#123;<span class="hljs-doctag">@link</span> Stable&#125; is safe here, because value is never null.</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Stable</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">byte</span>[] value;<br><br>    .................<br>&#125;<br></code></pre></td></tr></table></figure>

<p>需要注意的是，这个字符串使用了 <code>final</code> 关键字修饰。</p>
<p>我们都知道被 <code>final</code> 关键字修饰的类不能被继承、修饰的方法不能被重写、修饰的基本数据类型变量的值不能被改变，修饰的引用类型变量不能再指向其他对象。很显然，这里的 char [] 数组属于引用型变量，所以其内容是可以改变的</p>
<p>这就是很多人疑惑的点了：难道不是很奇怪吗？String 的内容是不可变的，但 String 底层是 final 修饰的 char[] 数组实现的，而这个数组内容是可变的。所以你给解释一下 String 内容不可变到底是怎么一回事</p>
<p>很多八股在这里都在扯淡，在这里我给出正确答案：这里的 char [] 数组属于引用型变量，理论上它的内容当然是可以改变的：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">final</span> String[] arr = <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[]&#123;<span class="hljs-string">&quot;Hello&quot;</span>, <span class="hljs-string">&quot;World&quot;</span>&#125;;<br><br>arr[<span class="hljs-number">0</span>] = <span class="hljs-string">&quot;Hi&quot;</span>; <span class="hljs-comment">// 这里是可以的，因为arr[0]指向的对象（即&quot;Hello&quot;）是可以改变的</span><br></code></pre></td></tr></table></figure>

<p>但是这一点跟 String 内容是不可变的本身没有冲突，因为 String 并没有对外提供任何方法，去改变内置的 char [] 数组的内容，所以 String 对外表现出的 String 内容不可变，这就是：<strong>String 对象一旦被创建，其内容就一定不可变</strong>的正确解释</p>
<p>综上所述，String 类是不可变的，这意味着一旦一个 String 对象被创建，它的内容就不能被修改。即使 String 底层是通过 final 修饰的 char 数组实现的，但是这个 char 数组的内容也不能被修改，因为 String 并没有对外提供任何方法，允许我们去改变内置的 char [] 数组的内容。因此，即使我们可以访问到 String 对象的底层 char 数组，我们也不能通过改变这个数组来修改 String 对象的内容。任何尝试修改 String 对象内容的操作都会返回一个新的 String 对象，而原来的 String 对象保持不变</p>
<p>查看源码你就能清楚地看到这个过程了，当然源码很复杂，这里展示出 <code>replace</code> 的部分源码，你可以看到在执行这个操作的过程中，是 new 了新的 byte [] 的：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">replace</span><span class="hljs-params">(<span class="hljs-type">byte</span>[] value, <span class="hljs-type">char</span> oldChar, <span class="hljs-type">char</span> newChar)</span> &#123;<br>    <span class="hljs-keyword">if</span> (canEncode(oldChar)) &#123;<br>        <span class="hljs-type">int</span> <span class="hljs-variable">len</span> <span class="hljs-operator">=</span> value.length;<br>        <span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> -<span class="hljs-number">1</span>;<br>        <span class="hljs-keyword">while</span> (++i &lt; len) &#123;<br>            <span class="hljs-keyword">if</span> (value[i] == (<span class="hljs-type">byte</span>)oldChar) &#123;<br>                <span class="hljs-keyword">break</span>;<br>            &#125;<br>        &#125;<br>        <span class="hljs-keyword">if</span> (i &lt; len) &#123;<br>            <span class="hljs-keyword">if</span> (canEncode(newChar)) &#123;<br>                <span class="hljs-type">byte</span> buf[] = <span class="hljs-keyword">new</span> <span class="hljs-title class_">byte</span>[len];<br>                <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">j</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; j &lt; i; j++) &#123;    <span class="hljs-comment">// TBD arraycopy?</span><br>                    buf[j] = value[j];<br>                &#125;<br>                <span class="hljs-keyword">while</span> (i &lt; len) &#123;<br>                    <span class="hljs-type">byte</span> <span class="hljs-variable">c</span> <span class="hljs-operator">=</span> value[i];<br>                    buf[i] = (c == (<span class="hljs-type">byte</span>)oldChar) ? (<span class="hljs-type">byte</span>)newChar : c;<br>                    i++;<br>                &#125;<br>                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(buf, LATIN1);<br>            &#125; <span class="hljs-keyword">else</span> &#123;<br>                <span class="hljs-type">byte</span>[] buf = StringUTF16.newBytesFor(len);<br>                <span class="hljs-comment">// inflate from latin1 to UTF16</span><br>                inflate(value, <span class="hljs-number">0</span>, buf, <span class="hljs-number">0</span>, i);<br>                <span class="hljs-keyword">while</span> (i &lt; len) &#123;<br>                    <span class="hljs-type">char</span> <span class="hljs-variable">c</span> <span class="hljs-operator">=</span> (<span class="hljs-type">char</span>)(value[i] &amp; <span class="hljs-number">0xff</span>);<br>                    StringUTF16.putChar(buf, i, (c == oldChar) ? newChar : c);<br>                    i++;<br>                &#125;<br>                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(buf, UTF16);<br>            &#125;<br>        &#125;<br>    &#125;<br>    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; <span class="hljs-comment">// for string to return this;</span><br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li>这样做有什么好处？这种不可变性是 Java String 类的一个重要特性，使得 String 可以安全地被共享和传递，而不需要担心其他部分的代码会修改它的内容</li>
</ul>
<h2 id="Java-基础"><a href="#Java-基础" class="headerlink" title="Java 基础"></a>Java 基础</h2><ul>
<li><h5 id="本人太菜，不定时巩固-Java-基础，今天巩固如下操作：（2023-08-14-早）"><a href="#本人太菜，不定时巩固-Java-基础，今天巩固如下操作：（2023-08-14-早）" class="headerlink" title="本人太菜，不定时巩固 Java 基础，今天巩固如下操作：（2023/08/14 早）"></a>本人太菜，不定时巩固 Java 基础，今天巩固如下操作：（2023/08/14 早）</h5><ul>
<li><p>Radom 随机数</p>
</li>
<li><p>Collections 排序</p>
</li>
<li><p>Stream 流遍历</p>
</li>
<li><p>System.currentTimeMillis() 计时</p>
</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// Radom 随机数</span><br><span class="hljs-type">Random</span> <span class="hljs-variable">random</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Random</span>();<br>     ArrayList&lt;Double&gt; arrayList = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;();<br>     <span class="hljs-type">long</span> <span class="hljs-variable">startTime</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>     <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100000</span>; i++) &#123;<br>         arrayList.add(random.nextDouble());<br>     &#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// Collections 排序</span><br><span class="hljs-comment">// 第一种方法</span><br>      Collections.sort(arrayList, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Comparator</span>&lt;Double&gt;() &#123;<br>          <span class="hljs-meta">@Override</span><br>          <span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">compare</span><span class="hljs-params">(Double o1, Double o2)</span> &#123;<br>              <span class="hljs-keyword">return</span> o1.compareTo(o2);<br>          &#125;<br>      &#125;);<br>      <span class="hljs-comment">// 第二种方法</span><br>      Collections.sort(arrayList, (o1, o2) -&gt; &#123;<br>          <span class="hljs-keyword">return</span> o1.compareTo(o2);<br>      &#125;);<br>      <span class="hljs-comment">// 第三种方法</span><br>      Collections.sort(arrayList, (o1, o2) -&gt; o1.compareTo(o2));<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// Stream 流便利</span><br>       <span class="hljs-comment">// 第一种方法</span><br>        arrayList.forEach(item -&gt; &#123;<br>            System.out.println(item);<br>        &#125;);<br><br>        <span class="hljs-comment">// 第二种方法</span><br>        arrayList.stream().forEach(item -&gt; System.out.println(item));<br>    &#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java">List&lt;Integer&gt; userList = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;();<br><br>       <span class="hljs-type">Random</span> <span class="hljs-variable">rand</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Random</span>();<br>       <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++) &#123;<br>           userList.add(rand.nextInt(<span class="hljs-number">1000</span>));<br>       &#125;<br><br>       List&lt;Integer&gt; userList2 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;();<br>       userList2.addAll(userList);<br><br>       <span class="hljs-type">Long</span> <span class="hljs-variable">startTime1</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>       userList2.stream().sorted(Comparator.comparing(Integer::intValue)).collect(Collectors.toList());<br>       System.out.println(<span class="hljs-string">&quot;stream.sort耗时：&quot;</span> + (System.currentTimeMillis() - startTime1) + <span class="hljs-string">&quot;ms&quot;</span>);<br><br>       <span class="hljs-type">Long</span> <span class="hljs-variable">startTime</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>       userList.sort(Comparator.comparing(Integer::intValue));<br>       System.out.println(<span class="hljs-string">&quot;List.sort()耗时：&quot;</span> + (System.currentTimeMillis() - startTime) + <span class="hljs-string">&quot;ms&quot;</span>);<br></code></pre></td></tr></table></figure>

<ul>
<li>有时间总结下 SpringBoot 中的定时任务：<a target="_blank" rel="noopener" href="https://juejin.cn/post/7158851405531316254">我终于会写 Java 的定时任务了！ - 掘金 (juejin.cn)</a></li>
</ul>
<h3 id="Date-Time-API"><a href="#Date-Time-API" class="headerlink" title="Date/Time API"></a>Date/Time API</h3><ul>
<li><h5 id="学习以下内容可以参考这篇博客：8-个你必须知道的-Java8-新特性，让你的代码变得优雅！-掘金-juejin-cn"><a href="#学习以下内容可以参考这篇博客：8-个你必须知道的-Java8-新特性，让你的代码变得优雅！-掘金-juejin-cn" class="headerlink" title="学习以下内容可以参考这篇博客：8 个你必须知道的 Java8 新特性，让你的代码变得优雅！ - 掘金 (juejin.cn)"></a>学习以下内容可以参考这篇博客：<a target="_blank" rel="noopener" href="https://juejin.cn/post/7240697872160833596">8 个你必须知道的 Java8 新特性，让你的代码变得优雅！ - 掘金 (juejin.cn)</a></h5></li>
<li><p>在 Java 8 中引入了全新的日期时间处理类库，使得处理日期时间变得更加方便和易于理解</p>
</li>
<li><h5 id="以下是-java-time-包中最重要的一些类：（2023-08-18-午）"><a href="#以下是-java-time-包中最重要的一些类：（2023-08-18-午）" class="headerlink" title="以下是 java.time 包中最重要的一些类：（2023/08/18 午）"></a>以下是 <code>java.time</code> 包中最重要的一些类：（2023/08/18 午）</h5><ul>
<li><code>LocalDate</code>：表示日期，如 2022 年 5 月 15 日。</li>
<li><code>LocalTime</code>：表示时间，如 13:45:30。</li>
<li><code>LocalDateTime</code>：代表日期和时间，比如 2022 年 5 月 15 日 13:45:30。</li>
<li><code>ZonedDateTime</code>：代表带有时区的日期时间，在上述所有日期时间类型的基础上，还提供了时区信息。</li>
<li><code>Duration</code>：在两个时间点之间表示时间量。</li>
<li><code>Period</code>：在两个日期之间表示天、周、月或年的数量。</li>
<li><code>DateTimeFormatter</code>：可以将日期时间对象按照指定的格式进行格式化或者解析。</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-comment">// 获取日期</span><br>    <span class="hljs-type">LocalDateTime</span> <span class="hljs-variable">dateTime</span> <span class="hljs-operator">=</span> LocalDateTime.now();<br>    System.out.println(dateTime);<br>    <span class="hljs-comment">// 日期格式</span><br>    <span class="hljs-type">DateTimeFormatter</span> <span class="hljs-variable">pattern</span> <span class="hljs-operator">=</span> DateTimeFormatter.ofPattern(<span class="hljs-string">&quot;yyyy-MM-dd HH:mm:ss&quot;</span>);<br>    <span class="hljs-type">String</span> <span class="hljs-variable">format</span> <span class="hljs-operator">=</span> dateTime.format(pattern);<br>    System.out.println(format);<br><br>    <span class="hljs-type">String</span> <span class="hljs-variable">now</span> <span class="hljs-operator">=</span> LocalDateTime.now().format(DateTimeFormatter.ofPattern(<span class="hljs-string">&quot;yyyy年MM月dd日 HH时mm分ss秒&quot;</span>));<br>    System.out.println(now);<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test2</span><span class="hljs-params">()</span> &#123;<br>     <span class="hljs-comment">// 获取日期</span><br>     <span class="hljs-type">Duration</span> <span class="hljs-variable">between</span> <span class="hljs-operator">=</span> Duration.between(LocalDate.of(<span class="hljs-number">2022</span>, Month.JULY, <span class="hljs-number">10</span>), LocalDate.now());<br>     System.out.println(between);<br>     <span class="hljs-type">Duration</span> <span class="hljs-variable">between1</span> <span class="hljs-operator">=</span> Duration.between(LocalTime.of(<span class="hljs-number">12</span>, <span class="hljs-number">29</span>, <span class="hljs-number">10</span>), LocalDate.now());<br>     System.out.println(between1);<br> &#125;<br></code></pre></td></tr></table></figure>

<h3 id="Optional-容器类型"><a href="#Optional-容器类型" class="headerlink" title="Optional 容器类型"></a>Optional 容器类型</h3><blockquote>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7302322661957845028">null 不好，我真的推荐你使用 Optional - 掘金 (juejin.cn)</a></p>
<p>那么使用 <code>Optional</code> 容器类型 有什么好处呢？<strong>（2023/11/29 午）</strong></p>
<ul>
<li>可以避免空指针异常，提高代码的健壮性和可读性。</li>
<li>可以减少显式的空值检查和 null 的使用，使代码更简洁和优雅。</li>
<li>可以利用函数式编程的特性，实现更灵活和高效的逻辑处理。</li>
<li>可以提高代码的可测试性，方便进行单元测试和集成测试。</li>
</ul>
</blockquote>
<ul>
<li><p><code>ofNullable()</code> 方法：创建一个可能包含 null 值的 Optional 对象（2023/08/18 午）</p>
</li>
<li><p><code>isPresent()</code> 方法：判断 Optional 中是否存在值。返回 ture 表示存在值 返回 false 表示为 null</p>
</li>
<li><p><code>get()</code> 方法：如果 Optional 的值存在则返回该值，否则抛出 NoSuchElementException 异常。</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test3</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-comment">// 判断值是否为空</span><br>    Optional&lt;String&gt; optional = Optional.of(<span class="hljs-string">&quot;hhh&quot;</span>);<br>    <span class="hljs-keyword">if</span> (optional.isPresent()) &#123;<br>        System.out.println(optional.get());<br>    &#125;<br><br>    Optional.of(<span class="hljs-string">&quot;hhh&quot;</span>).ifPresent(System.out::println);<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test4</span><span class="hljs-params">(User user)</span> &#123;<br>    Optional.ofNullable(user).map(User::getName).ifPresentOrElse(out::println, user != <span class="hljs-literal">null</span> ?      user.setName(<span class="hljs-string">&quot;ccc&quot;</span>) : <span class="hljs-literal">null</span>);<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 创建一个空的 Optional 对象</span><br>Optional&lt;String&gt; empty = Optional.empty();<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 创建一个非空的 Optional 对象</span><br>Optional&lt;String&gt; hello = Optional.of(<span class="hljs-string">&quot;Hello&quot;</span>);<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 创建一个可能为空的 Optional 对象</span><br>Optional&lt;String&gt; name = Optional.ofNullable(<span class="hljs-string">&quot;Hello&quot;</span>);<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 使用 isPresent 和 get 方法</span><br>Optional&lt;String&gt; name1 = Optional.ofNullable(<span class="hljs-string">&quot;tom&quot;</span>);<br><span class="hljs-keyword">if</span> (name.isPresent()) &#123;<br>System.out.println(<span class="hljs-string">&quot;Hello, &quot;</span> + name1.get());<br>&#125; <span class="hljs-keyword">else</span> &#123;<br>System.out.println(<span class="hljs-string">&quot;Name is not available&quot;</span>);<br>&#125;<br><span class="hljs-comment">// 输出：Hello tom</span><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><br><span class="hljs-comment">// 使用 ifPresent(Consumer&lt;? super T&gt; action)</span><br>Optional&lt;String&gt; name2 = Optional.ofNullable(<span class="hljs-string">&quot;tom&quot;</span>);<br>name.ifPresent(s -&gt; &#123;<br>System.out.println(<span class="hljs-string">&quot;Hello, &quot;</span> + name2.get());<br>&#125;);<br><span class="hljs-comment">// 输出：Hello tom</span><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><br><span class="hljs-comment">// 使用 orElse(T other)</span><br>Optional&lt;String&gt; name3 = Optional.ofNullable(<span class="hljs-literal">null</span>);<br><span class="hljs-type">String</span> <span class="hljs-variable">greeting</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;Hello, &quot;</span> + name3.orElse(<span class="hljs-string">&quot;Guest&quot;</span>);<br>System.out.println(greeting);<br><span class="hljs-comment">// 输出：Hello Guest</span><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><br><span class="hljs-comment">// 使用 orElseThrow(Supplier&lt;? extends X&gt; exceptionSupplier)</span><br>Optional&lt;String&gt; name4 = Optional.ofNullable(<span class="hljs-string">&quot;null&quot;</span>);<br><span class="hljs-type">String</span> <span class="hljs-variable">greeting2</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;Hello, &quot;</span> + name4.orElseThrow(() -&gt; <span class="hljs-keyword">new</span> <span class="hljs-title class_">NullPointerException</span>(<span class="hljs-string">&quot;null&quot;</span>));<br><span class="hljs-comment">// 抛出 java.lang.NullPointerException: null 异常</span><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><br><span class="hljs-comment">// 使用 map(Function&lt;? super T,? extends U&gt; mapper)</span><br>Optional&lt;String&gt; name5 = Optional.ofNullable(<span class="hljs-string">&quot;tom&quot;</span>);<br><span class="hljs-type">String</span> <span class="hljs-variable">greeting3</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;Hello, &quot;</span> + name5.map(s -&gt; s.toUpperCase()).get();<br>System.out.println(greeting3);<br><span class="hljs-comment">// 输出：Hello TOM</span><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 使用 flatMap(Function&lt;? super T,Optional&lt;U&gt;&gt; mapper)</span><br>Optional&lt;String&gt; name6 = Optional.ofNullable(<span class="hljs-string">&quot;tom&quot;</span>);<br><span class="hljs-type">String</span> <span class="hljs-variable">greeting4</span> <span class="hljs-operator">=</span> name6.flatMap(s -&gt; Optional.of(<span class="hljs-string">&quot;Hello &quot;</span> + s)).get();<br>System.out.println(greeting4);<br><span class="hljs-comment">// 输出：Hello tom</span><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// filter(Predicate&lt;? super T&gt; predicate)</span><br>Optional&lt;String&gt; name7 = Optional.ofNullable(<span class="hljs-string">&quot;tom&quot;</span>);<br><span class="hljs-type">String</span> <span class="hljs-variable">greeting5</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;Hello &quot;</span> + name7.filter(s -&gt; !s.isEmpty()).get();<br>System.out.println(greeting5);<br><span class="hljs-comment">// 输出：Hello tom</span><br></code></pre></td></tr></table></figure>

<h3 id="BigDecimal"><a href="#BigDecimal" class="headerlink" title="BigDecimal"></a>BigDecimal</h3><ul>
<li><h5 id="使用-BigDecimal-需要注意的-6-点：（2023-08-19-早）"><a href="#使用-BigDecimal-需要注意的-6-点：（2023-08-19-早）" class="headerlink" title="使用 BigDecimal 需要注意的 6 点：（2023/08/19 早）"></a>使用 BigDecimal 需要注意的 6 点：（2023/08/19 早）</h5><ul>
<li>构造初始化</li>
<li>加减乘除</li>
<li>设置精度</li>
<li>去掉末尾的 0</li>
<li>转换为 Double</li>
<li>比较大小</li>
</ul>
</li>
<li><p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7207617774633320504#comment">BigDecimal 不可触碰的 6 个坑 - 掘金 (juejin.cn)</a></p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test5</span><span class="hljs-params">()</span> &#123;<br>      <span class="hljs-comment">// 1.两个BigDecimal对象</span><br>      <span class="hljs-type">BigDecimal</span> <span class="hljs-variable">dec1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BigDecimal</span>(<span class="hljs-string">&quot;1.3&quot;</span>);<br>      <span class="hljs-type">BigDecimal</span> <span class="hljs-variable">dec2</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BigDecimal</span>(<span class="hljs-string">&quot;1.3&quot;</span>);<br><br>      <span class="hljs-comment">// 2.加减乘除</span><br>      <span class="hljs-type">BigDecimal</span> <span class="hljs-variable">add</span> <span class="hljs-operator">=</span> dec1.add(dec2);<br>      <span class="hljs-type">BigDecimal</span> <span class="hljs-variable">subtract</span> <span class="hljs-operator">=</span> dec1.subtract(dec2);<br>      <span class="hljs-type">BigDecimal</span> <span class="hljs-variable">multiply</span> <span class="hljs-operator">=</span> dec1.multiply(dec2);<br><br>      <span class="hljs-comment">// 3.设置精度</span><br>      <span class="hljs-type">BigDecimal</span> <span class="hljs-variable">divide</span> <span class="hljs-operator">=</span> dec1.divide(dec2).setScale(<span class="hljs-number">3</span>, RoundingMode.HALF_DOWN);<br><br>      out.println(add);<br>      out.println(subtract);<br>      out.println(multiply);<br>      System.out.println(divide);<br><br>      <span class="hljs-comment">// 4.去掉末尾的0</span><br>      <span class="hljs-type">BigDecimal</span> <span class="hljs-variable">bigDecimal</span> <span class="hljs-operator">=</span> divide.stripTrailingZeros();<br>      out.println(bigDecimal);<br><br>      <span class="hljs-comment">// 5.转换为Double</span><br>      <span class="hljs-type">BigDecimal</span> <span class="hljs-variable">value</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BigDecimal</span>(<span class="hljs-string">&quot;1888977466432.1241341341413414&quot;</span>);<br>      <span class="hljs-type">double</span> <span class="hljs-variable">doubleValue</span> <span class="hljs-operator">=</span> value.doubleValue();<br>      out.println(doubleValue);<br>      <span class="hljs-type">double</span> <span class="hljs-variable">doubleValue1</span> <span class="hljs-operator">=</span> value.toBigInteger().doubleValue();<br>      out.println(doubleValue1);<br><br>      <span class="hljs-comment">// 6.比较大小</span><br>      out.println(add.compareTo(subtract) &gt; <span class="hljs-number">0</span> ? <span class="hljs-string">&quot;add大于sub&quot;</span> : <span class="hljs-string">&quot;add小于sub&quot;</span>);<br>  &#125;<br></code></pre></td></tr></table></figure>

<h3 id="数组转字符串"><a href="#数组转字符串" class="headerlink" title="数组转字符串"></a>数组转字符串</h3><ul>
<li><p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/haohaiyou/p/16806399.html">JAVA 数组转字符串 - 一只忙碌的蜗牛 - 博客园 (cnblogs.com)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_35939148/article/details/131495097">Java 中数组转字符串的五种方式效率比较_java 数组转字符串_likaistart 的博客-CSDN 博客</a></p>
</li>
<li><h5 id="数组如何转字符串？我们有好多种方案"><a href="#数组如何转字符串？我们有好多种方案" class="headerlink" title="数组如何转字符串？我们有好多种方案"></a>数组如何转字符串？我们有好多种方案</h5></li>
<li><h5 id="我们经常能遇到这样的情景："><a href="#我们经常能遇到这样的情景：" class="headerlink" title="我们经常能遇到这样的情景："></a>我们经常能遇到这样的情景：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 创建一个List</span><br>List&lt;String&gt; list = Arrays.asList(<span class="hljs-string">&quot;Apple&quot;</span>, <span class="hljs-string">&quot;Banana&quot;</span>, <span class="hljs-string">&quot;Cherry&quot;</span>, <span class="hljs-string">&quot;Date&quot;</span>, <span class="hljs-string">&quot;Elderberry&quot;</span>);<br><span class="hljs-comment">// 创建一个String数组</span><br>String[] arr = &#123;<span class="hljs-string">&quot;0&quot;</span>, <span class="hljs-string">&quot;1&quot;</span>, <span class="hljs-string">&quot;2&quot;</span>, <span class="hljs-string">&quot;3&quot;</span>, <span class="hljs-string">&quot;4&quot;</span>, <span class="hljs-string">&quot;5&quot;</span>&#125;;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="如果是-List，则有如下转字符串的方法：（2023-09-20-晚）"><a href="#如果是-List，则有如下转字符串的方法：（2023-09-20-晚）" class="headerlink" title="如果是 List，则有如下转字符串的方法：（2023/09/20 晚）"></a>如果是 List，则有如下转字符串的方法：（2023/09/20 晚）</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// stream流</span><br><span class="hljs-type">String</span> <span class="hljs-variable">collect</span> <span class="hljs-operator">=</span> list.stream().collect(Collectors.joining(<span class="hljs-string">&quot;,&quot;</span>));<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//org.apache.commons.lang3包</span><br><span class="hljs-type">String</span> <span class="hljs-variable">join</span> <span class="hljs-operator">=</span>  StringUtils.join(longList, <span class="hljs-string">&quot;,&quot;</span>);<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// StringBuilder</span><br><span class="hljs-type">StringBuilder</span> <span class="hljs-variable">sb</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuilder</span>();<br>        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; list.size(); i++) &#123;<br>            sb.append(i);<br>            <span class="hljs-keyword">if</span> (i &lt; list.size() - <span class="hljs-number">1</span>) &#123;<br>                sb.append(<span class="hljs-string">&quot;,&quot;</span>);<br>         &#125;<br>   &#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// String.join</span><br><span class="hljs-type">String</span> <span class="hljs-variable">join</span> <span class="hljs-operator">=</span> String.join(<span class="hljs-string">&quot;,&quot;</span>, list);<br></code></pre></td></tr></table></figure>

<ul>
<li><p>以上方法，转换所消耗的<strong>时间越来越少</strong>，<strong>效率越来越高</strong></p>
</li>
<li><h5 id="如果是-String-，则有如下转字符串的方法：（2023-09-20-晚）"><a href="#如果是-String-，则有如下转字符串的方法：（2023-09-20-晚）" class="headerlink" title="如果是 String[]，则有如下转字符串的方法：（2023/09/20 晚）"></a>如果是 String[]，则有如下转字符串的方法：（2023/09/20 晚）</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//org.apache.commons.lang3包</span><br><span class="hljs-type">String</span> <span class="hljs-variable">join</span> <span class="hljs-operator">=</span>  StringUtils.join(longList, <span class="hljs-string">&quot;,&quot;</span>);<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// String.join</span><br><span class="hljs-type">String</span> <span class="hljs-variable">join</span> <span class="hljs-operator">=</span> String.join(<span class="hljs-string">&quot;,&quot;</span>, list);<br></code></pre></td></tr></table></figure>

<h3 id="Java8-中的-Map-函数"><a href="#Java8-中的-Map-函数" class="headerlink" title="Java8 中的 Map 函数"></a>Java8 中的 Map 函数</h3><h4 id="computeIfAbsent"><a href="#computeIfAbsent" class="headerlink" title="computeIfAbsent"></a>computeIfAbsent</h4><ul>
<li>如果指定的 key 不存在于 Map 中，那么会执行指定的函数来计算并将结果作为 value 放入到 Map 中。</li>
<li>如果指定的 key 已经存在于 Map 中，则不会执行计算函数，而是直接返回已存在的 value。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.util.HashMap;<br><span class="hljs-keyword">import</span> java.util.Map;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ComputeIfAbsentExample</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        Map&lt;String, Integer&gt; map = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>        map.put(<span class="hljs-string">&quot;apple&quot;</span>, <span class="hljs-number">1</span>);<br>        map.put(<span class="hljs-string">&quot;banana&quot;</span>, <span class="hljs-number">2</span>);<br><br>        <span class="hljs-comment">// 当key不存在时，执行计算函数并将结果放入Map</span><br>        map.computeIfAbsent(<span class="hljs-string">&quot;orange&quot;</span>, key -&gt; &#123;<br>            System.out.println(<span class="hljs-string">&quot;Performing computation for orange&quot;</span>);<br>            <span class="hljs-keyword">return</span> key.length();<br>        &#125;);<br><br>        <span class="hljs-comment">// 当key已存在时，不执行计算函数，直接返回已存在的value</span><br>        map.computeIfAbsent(<span class="hljs-string">&quot;apple&quot;</span>, key -&gt; &#123;<br>            System.out.println(<span class="hljs-string">&quot;Performing computation for apple&quot;</span>);<br>            <span class="hljs-keyword">return</span> key.length();<br>        &#125;);<br><br>        System.out.println(map);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h4 id="computeIfPresent"><a href="#computeIfPresent" class="headerlink" title="computeIfPresent"></a>computeIfPresent</h4><ul>
<li>如果指定的 key 存在于 Map 中，那么会执行指定的函数来计算并将结果作为新的 value 放入到 Map 中。</li>
<li>如果指定的 key 不存在于 Map 中，则不会执行计算函数，什么也不做</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.util.HashMap;<br><span class="hljs-keyword">import</span> java.util.Map;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ComputeIfPresentExample</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        Map&lt;String, Integer&gt; map = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>        map.put(<span class="hljs-string">&quot;apple&quot;</span>, <span class="hljs-number">1</span>);<br>        map.put(<span class="hljs-string">&quot;banana&quot;</span>, <span class="hljs-number">2</span>);<br><br>        map.computeIfPresent(<span class="hljs-string">&quot;apple&quot;</span>, (key, value) -&gt; value * <span class="hljs-number">2</span>);<br>        map.computeIfPresent(<span class="hljs-string">&quot;orange&quot;</span>, (key, value) -&gt; value * <span class="hljs-number">2</span>);<br><br>        System.out.println(map);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h4 id="compute"><a href="#compute" class="headerlink" title="compute"></a>compute</h4><ul>
<li>而<code>compute()</code>函数无论旧的 value 是否为<code>null</code>，都会调用计算函数来计算新的 value，并将计算结果更新到 Map 中。</li>
<li>如果计算结果为<code>null</code>，则会将对应的 key 从 Map 中移除。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.util.HashMap;<br><span class="hljs-keyword">import</span> java.util.Map;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ComputeExample</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        Map&lt;String, Integer&gt; map = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>        map.put(<span class="hljs-string">&quot;apple&quot;</span>, <span class="hljs-number">1</span>);<br>        map.put(<span class="hljs-string">&quot;banana&quot;</span>, <span class="hljs-literal">null</span>);<br><br>        <span class="hljs-comment">// 使用computeIfPresent()</span><br>        map.computeIfPresent(<span class="hljs-string">&quot;apple&quot;</span>, (key, value) -&gt; value * <span class="hljs-number">2</span>);<br>        map.computeIfPresent(<span class="hljs-string">&quot;banana&quot;</span>, (key, value) -&gt; value * <span class="hljs-number">2</span>);<br>        System.out.println(map); <span class="hljs-comment">// 输出: &#123;apple=2&#125;</span><br><br>        <span class="hljs-comment">// 使用compute()</span><br>        map.compute(<span class="hljs-string">&quot;apple&quot;</span>, (key, value) -&gt; value + <span class="hljs-number">3</span>);<br>        map.compute(<span class="hljs-string">&quot;banana&quot;</span>, (key, value) -&gt; value + <span class="hljs-number">3</span>);<br>        System.out.println(map); <span class="hljs-comment">// 输出: &#123;apple=5, banana=null&#125;</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h4 id="merge"><a href="#merge" class="headerlink" title="merge"></a>merge</h4><ul>
<li>这个目前还不太明白，按下不表：</li>
</ul>
<figure class="highlight livescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs livescript"><span class="hljs-keyword">import</span> java.util.HashMap;<br><span class="hljs-keyword">import</span> java.util.<span class="hljs-built_in">Map</span>;<br><br>public <span class="hljs-keyword">class</span> <span class="hljs-title class_">MergeExample</span> &#123;<br>    public <span class="hljs-keyword">static</span> <span class="hljs-literal">void</span> main(<span class="hljs-built_in">String</span>[] args) &#123;<br>        <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, Integer&gt; map1 = <span class="hljs-keyword">new</span> HashMap&lt;&gt;();<br>        map1.put(<span class="hljs-string">&quot;apple&quot;</span>, <span class="hljs-number">1</span>);<br>        map1.put(<span class="hljs-string">&quot;banana&quot;</span>, <span class="hljs-number">2</span>);<br><br>        <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, Integer&gt; map2 = <span class="hljs-keyword">new</span> HashMap&lt;&gt;();<br>        map2.put(<span class="hljs-string">&quot;apple&quot;</span>, <span class="hljs-number">5</span>);<br>        map2.put(<span class="hljs-string">&quot;orange&quot;</span>, <span class="hljs-number">3</span>);<br><br>        map2.forEach<span class="hljs-function"><span class="hljs-params">((key, value) -&gt; &#123;</span></span><br><span class="hljs-params"><span class="hljs-function">            map1.merge(key, value, (oldValue, newValue) -&gt; oldValue + newValue);</span></span><br><span class="hljs-params"><span class="hljs-function">        &#125;)</span>;</span><br><span class="hljs-function"></span><br><span class="hljs-function">        <span class="hljs-title">System</span>.<span class="hljs-title">out</span>.<span class="hljs-title">println</span><span class="hljs-params">(map1)</span>; // 输出: &#123;<span class="hljs-title">orange</span>=3, <span class="hljs-title">apple</span>=6, <span class="hljs-title">banana</span>=2&#125;</span><br><span class="hljs-function">    &#125;</span><br><span class="hljs-function">&#125;</span><br></code></pre></td></tr></table></figure>

<h4 id="getOrDefalut"><a href="#getOrDefalut" class="headerlink" title="getOrDefalut"></a>getOrDefalut</h4><ul>
<li>当 key 存在时，取对应 value，否则取默认值</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-title function_">firstUniqChar</span><span class="hljs-params">(String s)</span> &#123;<br>       <span class="hljs-keyword">if</span> (s == <span class="hljs-literal">null</span> || s.length() == <span class="hljs-number">0</span>) &#123;<br>           <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>       &#125;<br>       Map&lt;Character, Integer&gt; frequency = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;Character, Integer&gt;();<br>       <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; s.length(); ++i) &#123;<br>           <span class="hljs-type">char</span> <span class="hljs-variable">ch</span> <span class="hljs-operator">=</span> s.charAt(i);<br>           frequency.put(ch, frequency.getOrDefault(ch, <span class="hljs-number">0</span>) + <span class="hljs-number">1</span>);<br>       &#125;<br>       <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; s.length(); ++i) &#123;<br>           <span class="hljs-keyword">if</span> (frequency.get(s.charAt(i)) == <span class="hljs-number">1</span>) &#123;<br>               <span class="hljs-keyword">return</span> i;<br>           &#125;<br>       &#125;<br>       <span class="hljs-keyword">return</span> -<span class="hljs-number">1</span>;<br>   &#125;<br></code></pre></td></tr></table></figure>

<h4 id="putIfAbsent"><a href="#putIfAbsent" class="headerlink" title="putIfAbsent"></a>putIfAbsent</h4><ul>
<li>与 put 的区别：如果有重复 key，保存的是<strong>最早存入的键值对</strong> <strong>（2023/10/08 早）</strong></li>
</ul>
<h4 id="forEach"><a href="#forEach" class="headerlink" title="forEach"></a>forEach</h4><h3 id="Java8-中的-Stream-流函数"><a href="#Java8-中的-Stream-流函数" class="headerlink" title="Java8 中的 Stream 流函数"></a>Java8 中的 Stream 流函数</h3><h4 id="groupingBy"><a href="#groupingBy" class="headerlink" title="groupingBy"></a>groupingBy</h4><ul>
<li>将集合中的元素，<strong>按某个属性进行分组</strong>，<strong>返回的结果</strong>是一个 <strong>Map 集合</strong> <strong>（2023/10/08 早）</strong></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Person</span> &#123;<br>    <span class="hljs-keyword">private</span> <span class="hljs-type">int</span> age;<br>    <span class="hljs-keyword">private</span> String name;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">Person</span><span class="hljs-params">(<span class="hljs-type">int</span> age, String name)</span> &#123;<br>        <span class="hljs-built_in">this</span>.age = age;<br>        <span class="hljs-built_in">this</span>.name = name;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">GroupingByExample</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        List&lt;Person&gt; people = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;();<br>        people.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-number">25</span>, <span class="hljs-string">&quot;Alice&quot;</span>));<br>        people.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-number">30</span>, <span class="hljs-string">&quot;Bob&quot;</span>));<br>        people.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-number">25</span>, <span class="hljs-string">&quot;Charlie&quot;</span>));<br>        people.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-number">40</span>, <span class="hljs-string">&quot;David&quot;</span>));<br>        people.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-number">30</span>, <span class="hljs-string">&quot;Emily&quot;</span>));<br><br>        Map&lt;Integer, List&lt;Person&gt;&gt; groups = people.stream()<br>                .collect(Collectors.groupingBy(Person::getAge));<br><br>        System.out.println(groups);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="泛型理解"><a href="#泛型理解" class="headerlink" title="泛型理解"></a>泛型理解</h3><ul>
<li><h5 id="推荐学习：我真的了解泛型吗？-掘金-juejin-cn"><a href="#推荐学习：我真的了解泛型吗？-掘金-juejin-cn" class="headerlink" title="推荐学习：我真的了解泛型吗？ - 掘金 (juejin.cn)"></a>推荐学习：<a target="_blank" rel="noopener" href="https://juejin.cn/post/7263810695459504189#heading-8">我真的了解泛型吗？ - 掘金 (juejin.cn)</a></h5></li>
<li><h5 id="学习-Java-泛型：（2023-10-13-晚）"><a href="#学习-Java-泛型：（2023-10-13-晚）" class="headerlink" title="学习 Java 泛型：（2023/10/13 晚）"></a>学习 Java 泛型：（2023/10/13 晚）</h5><ul>
<li>泛型类</li>
<li>泛型接口</li>
<li>泛型方法：返回值、入参位置、方法体中</li>
<li>个人感受：泛型很好理解：我们经常讲到一个对象实例的就是以类作为模板创建的，那么也可以讲一个普通类可以以泛型类作为模板；那么泛型是用来干嘛的呢，我们为什么要使用泛型呢？其实，所有的泛型类在编译后生成的字节码与普通类无异，因为在编译前，所有泛型类型就被擦除了。所以我们可以把泛型看作一个语法糖，将类型转换的校验提前在编译时，减少类型转换错误的发生，使编写的程序更加具有健壮性。</li>
</ul>
</li>
<li><h5 id="我觉得以下这段总结更妙："><a href="#我觉得以下这段总结更妙：" class="headerlink" title="我觉得以下这段总结更妙："></a>我觉得以下这段总结更妙：</h5></li>
</ul>
<blockquote>
<p>泛型是 Java 语言中的一项强大的特性，它允许在编译时指定类、接口或方法的参数类型，从而在编译阶段就能够进行类型检查。这样可以减少类型转换的错误，并提高代码的安全性和可读性。</p>
<p>通过使用泛型，我们可以在编译时捕捉到一些类型错误，而不是在运行时才发现，这样可以避免一些潜在的 bug。泛型还可以增加代码的可重用性和灵活性，因为泛型类、接口和方法可以用于多种不同的类型，而无需针对每一种类型单独编写或重复编写相似的代码。</p>
<p>总的来说，通过使用泛型，我们可以在编写 Java 代码时更好地约束和使用类型信息，减少类型错误，提高代码的可读性和健壮性。</p>
</blockquote>
<ul>
<li><h5 id="了解泛型的实现原理，理解泛型的使用方式，更加加深了我对-Java-语言的理解"><a href="#了解泛型的实现原理，理解泛型的使用方式，更加加深了我对-Java-语言的理解" class="headerlink" title="了解泛型的实现原理，理解泛型的使用方式，更加加深了我对 Java 语言的理解"></a>了解泛型的实现原理，理解泛型的使用方式，更加加深了我对 Java 语言的理解</h5></li>
</ul>
<h2 id="JVM"><a href="#JVM" class="headerlink" title="JVM"></a>JVM</h2><blockquote>
<p>🥣 推荐阅读：<a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/624513744">jvm 内存模型（运行时数据区）简介 - 知乎 (zhihu.com)</a> <strong>（2023/12/13）</strong></p>
</blockquote>
<h1 id="锦绣收官"><a href="#锦绣收官" class="headerlink" title="锦绣收官"></a>锦绣收官</h1>
                
              </div>
            
            <hr/>
            <div>
              <!-- 文章页面 - 底部标签 -->
<div class="post-metas my-3">
  
  <div class="post-meta mr-3 d-flex align-items-center">
    <i class="iconfont icon-category"></i>
    <!-- 归档页 - 文章列表 -->


<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/blog/categories/%E4%BB%A3%E7%A0%81%E9%94%8B%E8%8A%92/" class="category-chain-item">代码锋芒</a>
  
  

      </span>
    
  
</span>

  </div>
   
  <div class="post-meta">
    <i class="iconfont icon-tags"></i>
    
    <a href="/blog/tags/%E7%BA%BF%E7%A8%8B%E6%B1%A0/">#线程池</a>
    
    <a href="/blog/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/">#并发编程</a>
    
    <a href="/blog/tags/JUC/">#JUC</a>
    
    <a href="/blog/tags/%E7%BB%8F%E9%AA%8C/">#经验</a>
    
    <a href="/blog/tags/Java/">#Java</a>
    
  </div>
  
</div>


              <!-- 文章页面 - 底部标签下方 -->

  
  <div class="license-box my-3">
    <div class="license-title">
      <div>Java终极冒险：从基础到实战，解锁Java的神秘密码</div>
      <div>https://test.atomgit.net/blog/2023/10/24/Java终极冒险：从基础到实战，解锁Java的神秘密码/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>Memory</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2023年10月24日</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>更新于</div>
          <div>2025年7月31日</div>
        </div>
      
      
        <div class="license-meta-item">
          <div>许可协议</div>
          <div>
            
              
              
                <a target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
                  <span class="hint--top hint--rounded" aria-label="BY - 署名">
                    <i class="iconfont icon-by"></i>
                  </span>
                </a>
              
            
          </div>
        </div>
      
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/blog/2023/10/24/%E6%9E%84%E5%BB%BA%E9%AB%98%E5%8F%AF%E7%94%A8%E7%B3%BB%E7%BB%9F%EF%BC%9A%E7%AD%96%E7%95%A5%E4%B9%8B%E5%8C%99%EF%BC%8C%E8%A7%A3%E9%94%81%E6%95%B0%E5%AD%97%E4%B8%96%E7%95%8C%E6%97%A0%E9%9A%9C%E7%A2%8D%E4%B9%8B%E9%97%A8/" title="构建高可用系统：策略之匙，解锁数字世界无障碍之门">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">构建高可用系统：策略之匙，解锁数字世界无障碍之门</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/blog/2023/10/24/Redis%20%E9%A3%8E%E6%9A%B4%EF%BC%9A%E4%BB%8E%E5%8E%9F%E7%90%86%E5%88%B0%E5%BA%94%E7%94%A8%EF%BC%8C%E4%B8%80%E6%8E%A2%E5%86%85%E5%AD%98%E4%B8%AD%E7%9A%84%E6%95%B0%E6%8D%AE%E5%A5%A5%E7%A7%98/" title="Redis 风暴：从原理到应用，一探内存中的数据奥秘">
                        <span class="hidden-mobile">Redis 风暴：从原理到应用，一探内存中的数据奥秘</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            <!-- 文章页 - 评论上方 -->

  
  
    <article id="comments">
      
  <div id="valine"></div>
  <script type="text/javascript">
    Fluid.utils.loadComments('#valine', function() {
      Fluid.utils.createScript('https://cdn.jsdelivr.net/gh/HCLonely/Valine@latest/dist/Valine.min.js', function() {
        var options = Object.assign(
          {"appId":"T1hUHH9Ks1ggG9DBE3HgUPpV-gzGzoHsz","appKey":"XFkgzzvzNo3J3uNCbBiSOFKc","path":"window.location.pathname","placeholder":"填取QQ号和邮箱后评论，支持读取头像及消息提醒，留下点什么叭... ᶘ ᵒᴥᵒᶅ","avatar":"Gravatar","meta":["nick","mail","link"],"requiredFields":[],"pageSize":10,"lang":"zh-CN","highlight":true,"recordIP":false,"serverURLs":"https://t1huhh9k.lc-cn-n1-shared.com","emojiCDN":null,"emojiMaps":null,"enableQQ":true,"master":"8c9b6d6637904da62d74d019cc6982de","friends":"0188229a9cfc284176ea9135cb9514cc","visitor":true},
          {
            el: "#valine",
            path: window.location.pathname,
            app_id: "",
            app_key: "",
            placeholder: "填取QQ号和邮箱后评论，支持读取头像及消息提醒，留下点什么叭... ᶘ ᵒᴥᵒᶅ",
            avatar: "Gravatar",
            meta: ["nick","mail","link"],
            pageSize: "10",
            lang: "zh-CN",
            highlight: true,
            recordIP: false,
            serverURLs: "https://t1huhh9k.lc-cn-n1-shared.com",

            // 设置Bilibili表情包地址
            emojiCDN: '//i0.hdslb.com/bfs/emote/', 
            // 表情title和图片映射
            emojiMaps: {
                "tv_白眼": "48f75163437445665a9be80bb316e4cb252c5415.gif@48w_48h.webp",
                "tv_doge": "302d6c88c63ed162c81a49cafe7ed2709e6fb955.gif@48w_48h.webp",
                "tv_坏笑": "5d2572efd09aab5dde9e2a198bb3f9ac1e2a982e.gif@48w_48h.webp",
                "tv_难过": "9c6b41008a67755410f712334c64313df5f91b3f.gif@48w_48h.webp",
                "tv_生气": "1902a5a2df5b5c931d88c12f0feb264b1e109d0d.gif@48w_48h.webp",
                "tv_委屈": "af5a5853edb43a8178a8cb5df707fa5e88143699.gif@48w_48h.webp",
                "tv_斜眼笑": "c66568b471192ca1f62f6ed4384dc1b283ab7508.gif@48w_48h.webp",
                "tv_呆": "d3fa91e4db9215eb1e20ab9da44f1214aa4bda7b.gif@48w_48h.webp",
                "tv_发怒": "3959eb81b952e4fa8d269d98f9e3639172d84073.gif@48w_48h.webp",
                "tv_呕吐": "db58e9442aae26694af18cc1683607cca3a16763.gif@48w_48h.webp",
                "tv_思考": "b63f9146bfd985af014f8d6d4bdb498805be48f9.gif@48w_48h.webp",
                "tv_微笑": "b98656855d782f61cb8edc7f7fca6563ecafff7e.gif@48w_48h.webp",
                "tv_疑问": "fce1b1a0f3b0e39a2dc16a18508dba7b91e929f4.gif@48w_48h.webp",
                "tv_大哭": "cba61f05f3039b02a7ffc0dfcd9d7995df9fdd74.gif@48w_48h.webp",
                "tv_鼓掌": "be106e6b265883a9f28fbe10f7b765701e2618d4.gif@48w_48h.webp",
                "tv_抠鼻": "696d9f93e722144dc2a78aeffc569418fdf3d565.gif@48w_48h.webp",
                "tv_亲亲": "3534ea44ab74bd20352b88c245a06c4b4c46d271.gif@48w_48h.webp",
                "tv_调皮": "fcd967395fd14e4dd5829fa7e8a967ce23205e52.gif@48w_48h.webp",
                "tv_笑哭": "1c2fd1e8c9dde12812f86e5d4cbddd8993d98082.gif@48w_48h.webp",
                "tv_晕": "030040ec5c9ddc9e3d067658c4139e7314ab42f8.gif@48w_48h.webp",
                "tv_点赞": "30ecff401245fb56bcc1cf588d1809ac1ab1607c.gif@48w_48h.webp",
                "tv_害羞": "411a3e459e8580f5bfd9f639a408247c4b509935.gif@48w_48h.webp",
                "tv_睡着": "3c8b5e293261287a6203597e29b3de07df4d18c6.gif@48w_48h.webp",
                "tv_色": "a0c6d99ab0ab63b8648f5283ff72cec04b604828.gif@48w_48h.webp",
                "tv_吐血": "e17e4539e169d14a3389ff147afea760cebe5de5.gif@48w_48h.webp",
                "tv_无奈": "eb4cb5f07cfd177c7e6a7914316717e56d9cc1d0.gif@48w_48h.webp",
                "tv_再见": "344f61609ecce2008520dc8a977b6169215748a9.gif@48w_48h.webp",
                "tv_流汗": "390bccec65eaff536bd5bb2a0c5b8b0bdea47334.gif@48w_48h.webp",
                "tv_偷笑": "7f11e6f7f63e79112b833bd41fa13a83d7cd8474.gif@48w_48h.webp",
                "tv_抓狂":"a476b93ecd8e94ac3257323fd822f91cef212de2.gif@48w_48h.webp",
                "tv_黑人问号":"b609adf664be33224a9923262031165ae3e34cd2.gif@48w_48h.webp",
                "tv_困":"91c2bf34ecf842d7016c01d841db3d4074bd281f.gif@48w_48h.webp",
                "tv_打脸":"b0fad4856e59c1240e448437da3287bb5ce547e5.gif@48w_48h.webp",
                "tv_闭嘴":"a3fc5388b09e945be3f18fe23bfed5874a0285b7.gif@48w_48h.webp",
                "tv_鄙视":"293b5d459e6264ecf314d20937a936fa672ccd1e.gif@48w_48h.webp",
                "tv_腼腆":"30984e8264324f901d19bea85dada7103b695534.gif@48w_48h.webp",
                "tv_馋":"2525c5703c594e5f0752f68db8948773caebde47.gif@48w_48h.webp",
                "tv_可爱":"f92d20f76258bc5f33fc9d7c5e2a1d41fef19a7c.gif@48w_48h.webp",
                "tv_发财":"76131e52c9b033681b4c896c6024d29ef7ec7ec2.gif@48w_48h.webp",
                "tv_生病":"beb94829fe04f1a41bd6ca611e1f6ca9ca169afa.gif@48w_48h.webp",
                "tv_流鼻血":"8ef473f74a849420da712487b2f56ecca1f695f5.gif@48w_48h.webp",
                "tv_尴尬":"e0b84ef5ee3e5b8978e584c7c5a6550c51d15f84.gif@48w_48h.webp",
                "tv_大佬":"14ca0c05382b8741940942b2430b7a8d55c02f7e.gif@48w_48h.webp",
                "tv_流泪":"7e71cde7858f0cd50d74b0264aa26db612a8a167.png@48w_48h.webp",
                "tv_冷漠":"b9cbc755c2b3ee43be07ca13de84e5b699a3f101.png@48w_48h.webp",
                "tv_皱眉":"72ccad6679fea0d14cce648b4d818e09b8ffea2d.png@48w_48h.webp",
                "tv_鬼脸":"0ffbbddf8a94d124ca2f54b360bbc04feb6bbfea.png@48w_48h.webp",
                "tv_调侃":"4bc022533ef31544ca0d72c12c808cf4a1cce3e3.png@48w_48h.webp",
                "tv_目瞪口呆":"0b8cb81a68de5d5365212c99375e7ace3e7891b7.png@48w_48h.webp",
            },

            master: "8c9b6d6637904da62d74d019cc6982de",
            friends: "0188229a9cfc284176ea9135cb9514cc",
            tagMeta: ["博主","友人","访客"],
            visitor: true // 阅读量统计
          }
        )
        new Valine(options);
        Fluid.utils.waitElementVisible('#valine .vcontent', () => {
          var imgSelector = '#valine .vcontent img:not(.vemoji)';
          Fluid.plugins.imageCaption(imgSelector);
          Fluid.plugins.fancyBox(imgSelector);
        })
      });
    });
  </script>
  <noscript>Please enable JavaScript to view the comments</noscript>


    </article>
  


          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      <!-- 文章页面 - 右侧目录 -->

  <aside class="sidebar" style="margin-left: -1rem">
    <!-- 文章页面 - 右侧目录上方 -->
<div id="toc">
  <p class="toc-header">
    <i class="iconfont icon-list"></i>
    <span>目录</span>
  </p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>



    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <!-- 博客 - 搜索框 -->
<div
  class="modal fade"
  id="modalSearch"
  tabindex="-1"
  role="dialog"
  aria-labelledby="ModalLabel"
  aria-hidden="true"
>
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">
          搜索
        </h4>
        <button
          type="button"
          id="local-search-close"
          class="close"
          data-dismiss="modal"
          aria-label="Close"
        >
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input
            type="text"
            id="local-search-input"
            class="form-control validate"
          />
          <label data-error="x" data-success="v" for="local-search-input"
            >关键词</label
          >
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
        <div class="modal fade" id="modalNotice" tabindex="-1" role="dialog" aria-hidden="true"
     style="font-family: 'Ma Shan Zheng', cursive; color: #ffffff;">
    <div class="modal-dialog modal-dialog-scrollable" role="document"
         style="font-family: 'Ma Shan Zheng', cursive;">
        <div class="modal-content" style="border-radius: 6px;">
            <!-- 公告头部 - 响应式设计 -->
            <div class="modal-header text-center"
                 style="padding: 1rem 1.5rem;border-bottom: 1px solid rgba(255,255,255,0.2)">
                <h4 class="modal-title w-100 font-weight-bold"
                    style="font-family: 'Microsoft YaHei', sans-serif;letter-spacing: 1px;text-shadow: 1px 1px 2px rgba(0,0,0,0.2);color: #ffffff;
                           font-size: 1.5rem; /* 默认PC端大小 */
                           /* 移动端适配 */
                           @media (max-width: 768px) {
                               font-size: 1.2rem;
                           }">
                    <div class="notice-title">
                        <i class="fas fa-bullhorn mr-2"> 围栏告示</i>
                    </div>
                    <small style="display: block; font-family: 'STKaiti', serif; font-size: 0.6em; color: #a0a0a0; margin-top: 3px;">
                        旧梦轻拾 · 谨识
                    </small>
                </h4>
            </div>

            <!-- 公告内容区 - 响应式设计 -->
            <div class="modal-body"
                 style="padding: 2rem; /* PC端 */
                        max-height: 70vh;
                        overflow-y: auto;
                        /* 移动端适配 */
                        @media (max-width: 768px) {
                            padding: 1rem;
                            max-height: 60vh;
                        }">
                <!-- 图片容器（响应式设计） -->
                <!--                <div class="announcement-hero"-->
                <!--                     style="margin-bottom: 2rem; /* PC端 */-->
                <!--                            /* 移动端适配 */-->
                <!--                            @media (max-width: 768px) {-->
                <!--                                margin-bottom: 1rem;-->
                <!--                            }">-->
                <!--                    <img src="http://blog.memory-life.icu/%E3%80%90%E5%93%B2%E9%A3%8E%E5%A3%81%E7%BA%B8%E3%80%91%E5%8A%A8%E6%BC%AB-%E5%A4%95%E9%98%B3%E9%93%81%E8%BD%A8.jpg"-->
                <!--                         alt="时光溯洄题图"-->
                <!--                         style="width: 100%; -->
                <!--                                max-height: 400px; /* PC端 */-->
                <!--                                object-fit: cover;-->
                <!--                                border-radius: 4px; -->
                <!--                                box-shadow: 0 2px 12px rgba(0,0,0,0.1);-->
                <!--                                /* 移动端适配 */-->
                <!--                                @media (max-width: 768px) {-->
                <!--                                    max-height: 30vh;-->
                <!--                                }"-->
                <!--                         loading="lazy">-->
                <!--                </div>-->

                <!-- 静态公告列表 - 响应式设计 -->
                <div class="announcement-list">
                    <div class="announcement-item"
                         style="margin-bottom: 2rem; /* PC端 */
                                padding: 2rem;
                                background: rgba(255,255,255,0.05);
                                border-radius: 8px;
                                /* 移动端适配 */
                                @media (max-width: 768px) {
                                    margin-bottom: 1rem;
                                    padding: 1rem;
                                }">
                        <h2 style="color: #d38532;
                                  font-weight: 600;
                                  font-size: 1.5rem; /* PC端 */
                                  margin-bottom: 1.2rem;
                                  letter-spacing: 0.5px;
                                  /* 移动端适配 */
                                  @media (max-width: 768px) {
                                      font-size: 1.2rem;
                                      margin-bottom: 1rem;
                                  }">
                            <i class="fas fa-calendar-check mr-2"></i>时光溯洄
                            <small style="display: block; font-size: 0.6em; color: #b8b8b8;text-align: right;">——
                                晨昏往事</small>
                        </h2>
                        <div style="color: #e0e0e0;
                                  line-height: 1.8; /* PC端 */
                                  font-size: 1.05rem;
                                  letter-spacing: 0.3px;
                                  /* 移动端适配 */
                                  @media (max-width: 768px) {
                                      line-height: 1.6;
                                      font-size: 0.95rem;
                                  }">
                            <!-- 内容保持不变 -->
                            <p style="margin-bottom: 1rem; text-indent: 2em;">你好。</p>
                            <!-- 其余段落内容... -->
                            <p style="margin-bottom: 1rem; text-indent: 2em;">
                                建站后的第813天，在百忙之中总算又能抽出时间来，对整个博客站点功能做整体的规划及优化完善。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em;">
                                在刚刚过去的几年时间里，往事皆以笔墨挥毫于时光素笺之上，数百天如一日，故事的书写从未间断过。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em; font-style: italic; color: #b8b8b8;">
                                时光悠悠，日月如流，过往的蹉跎岁月总在不经意间在心间回眸。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em; font-weight: 500; color: #ffffff;">
                                俯仰之间，轻舟已过万重山。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em; font-weight: 500; color: #ffffff;">
                                蹉跎之处，往事皆成云烟散。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em;">
                                这几年的时光过得太快了，好像过去这段岁月里的所有事情都发生在一瞬间。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em;">
                                这几年的时光同样太久了，久到那些时日里仿佛囊括了自己的整个前半生。</p>
                            <p style="margin-bottom: 0; text-align: right; font-size: 0.95rem; color: #a0a0a0;">——
                                八百余个昼夜，当时只道是地久天长，如今回望，不过浮光掠影间的一瞬。</p>
                        </div>
                    </div>

                    <!-- 第二个公告项 - 响应式设计 -->
                    <div class="announcement-item"
                         style="margin-bottom: 2rem; /* PC端 */
                                padding: 2rem;
                                background: rgba(255,255,255,0.05);
                                border-radius: 8px;
                                /* 移动端适配 */
                                @media (max-width: 768px) {
                                    margin-bottom: 1rem;
                                    padding: 1rem;
                                }">
                        <h2 style="color: #6cc70b;
                                  font-weight: 600;
                                  font-size: 1.5rem; /* PC端 */
                                  margin-bottom: 1.2rem;
                                  letter-spacing: 0.5px;
                                  /* 移动端适配 */
                                  @media (max-width: 768px) {
                                      font-size: 1.2rem;
                                      margin-bottom: 1rem;
                                  }">
                            <i class="fas fa-book-open mr-2"></i>墨痕新注
                            <small style="display: block; font-size: 0.6em; color: #b8b8b8;text-align: right;">——
                                待起之章</small>
                        </h2>
                        <div style="color: #e0e0e0;
                                  line-height: 1.8; /* PC端 */
                                  font-size: 1.05rem;
                                  letter-spacing: 0.3px;
                                  /* 移动端适配 */
                                  @media (max-width: 768px) {
                                      line-height: 1.6;
                                      font-size: 0.95rem;
                                  }">
                            <!-- 列表内容 - 响应式设计 -->
                            <ul style="padding-left: 1.8rem; /* PC端 */
                                      margin-bottom: 1.5rem;
                                      /* 移动端适配 */
                                      @media (max-width: 768px) {
                                          padding-left: 1.5rem;
                                          margin-bottom: 1.2rem;
                                      }">
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">溯源计划：</span>新增博文"溯源计划"，旨在用先进的数字技术留住过去的旧时光，让家族的记忆在数字云端生长发芽，全站博文数量已达90+。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">围栏告示：</span>自去年夏天站点新增了“评论回复”功能后，至今时隔十余月，在芒种前夕本站久违地上线“围栏告示”功能，本公告栏会不定时更新，持续跟进记录最新站点功能调整日志，更多精彩内容请敬请期待。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">图片查看：</span>最近几周时间里，考虑到图文流量费用收取问题，暂时关闭了七牛云对象存储私有空间访问权限，故博文内的图文无法正常预览，择日考虑开启访问权限。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">壁纸下载：</span>后续计划实现壁纸下载功能，均为个人收集的共150+超高分辨率精美壁纸，提供在线预览以及下载功能。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">阅读体验：</span>规划完善文章内分页功能，并提供“最近更新”的文章列表，计划收录更新率较高的文章，譬如：阳台日记、叙事之外、岁月如歌、游戏人生等等，尽可能多的提升在线浏览和阅读体验。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">友链完善：</span>持续优化完善友链页面，整理归档个人常用的高质量网站合集，无偿分享给有缘人。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">音乐餐厅：</span>历经十余小时不间断调研和尝试，本站成功上线”音乐餐厅”功能，目前仅收录了个人在QQ音乐平台积累的两份歌单，再匆忙也要记得点播一首喜欢的音乐，没有音乐熏陶的心灵终究是充满缺憾的。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">三言两语：</span>每张照片都是时光的便签，随手一贴就记下了那天，本站结合Mamos实现并注入留言板功能，随缘记录那些藏在照片背后的故事。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">旧梦轻拾：</span>更新全站博客文章所属分类，共设有代码锋芒、部署之道、旧梦轻拾、人间片刻等十余种分类。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">方寸之间：</span>博客应用版发布，更便捷的阅读体验来了！现已正式推出独立的桌面应用程序与移动端应用程序，暂时通过网盘和代码库提供安装包。
                                </li>
                                <p style="margin-bottom: 0; text-align: right; font-size: 0.9rem; color: #8a8a8a;">
                                    此公告最后更新于：<span style="color: #6cc70b;">2025/09/30晚</span>
                                </p>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 公告页脚 - 响应式设计 -->
            <div class="modal-footer"
                 style="border-top: 1px solid rgba(255,255,255,0.2);
                        padding: 1rem 1.5rem; /* PC端 */
                        justify-content: space-between;
                        /* 移动端适配 */
                        @media (max-width: 768px) {
                            padding: 0.8rem;
                        }">
                <small style="color: #ffffff;
                            font-size: 0.9rem; /* PC端 */
                            /* 移动端适配 */
                            @media (max-width: 768px) {
                                font-size: 0.8rem;
                            }">
                    <i class="iconfont icon-pen"></i>
                    <i class="fas fa-info-circle mr-1"></i>芒种前夕 · 旅途第813天
                    · 2025/05/28
                </small>
                <button id="confirm-btn" type="button" class="btn btn-sm" data-dismiss="modal"
                        style="background: #a0a0a0;
                               color: #ffffff;
                               border-radius: 4px;
                               padding: 0.5rem 1.2rem; /* PC端 */
                               border: none;
                               font-size: 0.9rem;
                               /* 移动端适配 */
                               @media (max-width: 768px) {
                                   padding: 0.3rem 0.8rem;
                                   font-size: 0.85rem;
                               }">
                    <i class="fas fa-check mr-1" style="color: #ffffff;">心已阅</i>
                </button>
            </div>
        </div>
    </div>
</div>

<style>
  .notice-title {
    display: flex;
    align-items: center;
    white-space: nowrap;
  }

  .notice-title:before,
  .notice-title:after {
    content: "";
    flex: 1;
    height: 1px;
    background: #ddd; /* 横线颜色 */
    margin: 0 10px; /* 横线与文字的间距 */
  }
</style>


<style>
  /* 响应式模态框宽度 */
  @media (min-width: 992px) {
    #modalNotice .modal-dialog {
      max-width: 800px;
    }
  }

  @media (max-width: 991px) {
    #modalNotice .modal-dialog {
      margin: 0.5rem auto;
    }
  }
</style>

<script>
  // 页面加载完成后执行
  document.addEventListener('DOMContentLoaded', function () {
    // 检查是否首次访问
    if (!localStorage.getItem('hasShownPopup')) {
      // 动态加载notice.ejs内容
      fetch('_partials/notice.ejs')  // 替换为实际路径
        .then(response => response.text())
        .then(html => {
          // 插入到页面body末尾
          document.body.insertAdjacentHTML('beforeend', html);

          // 手动初始化Bootstrap模态框
          $('#modalNotice').modal('show');

          // 标记为已显示
          localStorage.setItem('hasShownPopup', 'true');

          // 监听关闭事件
          $('#modalNotice').on('hidden.bs.modal', function () {
            // 可选的后续处理
          });
        })
        .catch(error => {
          console.error('加载公告栏失败:', error);
          // 备用方案：显示简单弹窗
          const fallbackHTML = `
                <div class="modal-backdrop" style="/* 样式 */">
                    <div class="modal-content">/* 内容 */</div>
                </div>`;
          document.body.insertAdjacentHTML('beforeend', fallbackHTML);
        });
    }
  });
</script>

    

    
  </main>

  <footer>
    <!-- 博客 - 页脚 -->
<div class="footer-inner">
  
    <div class="footer-content">
       <a href="https://hexo.fluid-dev.com/docs/guide/" target="_blank" rel="nofollow noopener"> <span>Memory | 个人文档站点</span> </a>
<i class="iconfont icon-love"></i> <a href="https://hexo.fluid-dev.com/docs/guide/" target="_blank" rel="nofollow noopener"> <span>配置指南 | Hexo Fluid 用户手册</span> </a> 
    </div>
  

  

  
    <!-- 备案信 ICP for China -->
    <div class="beian">
  <span>
    <a href="http://beian.miit.gov.cn/" target="_blank" rel="nofollow noopener">
      京ICP证123456号
    </a>
  </span>
  
    
      <span>
        <a
          href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=12345678"
          rel="nofollow noopener"
          class="beian-police"
          target="_blank"
        >
          
            <span style="visibility: hidden; width: 0">|</span>
            <img src="/blog/img/police_beian.png" alt="police-icon"/>
          
          <span>京公网安备12345678号</span>
        </a>
      </span>
    
  
</div>

  

  

</div>

<!-- 评论框美化 -->
<style>
    #comments .veditor{
        min-height: 20vh;
        background-image: url(http://blog.memory-life.icu/pathway0716.jpg);
        background-size: contain;
        background-repeat: no-repeat;
        background-position: right;
        background-color: rgba(255,255,255,0);
        resize: none;}
</style>

<script>
    //   自定义邮箱审核规则
    document.body.addEventListener('click', function(e) {
        if (e.target.classList.contains('vsubmit')) {
            const email = document.querySelector('input[type=email]');
            const nick = document.querySelector('input[name=nick]');
            const reg = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
            if (!email.value || !nick.value || !reg.test(email.value)) {
                const str = `<div class="valert text-center"><div class="vtext">请填写正确的昵称和邮箱！</div></div>`;
                const vmark = document.querySelector('.vmark');
                vmark.innerHTML = str;
                vmark.style.display = 'block';
                setTimeout(function() {
                    vmark.style.display = 'none';
                    vmark.innerHTML = '';
                }, 2500);
            }
        }
    })
</script>



  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/blog/js/events.js" ></script>
<script  src="/blog/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>







  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.18.2/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init(Object.assign({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      scrollSmooth    : true,
      includeTitleTags: true,
      headingsOffset  : -boardTop,
    }, CONFIG.toc));
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }

    Fluid.events.registerRefreshCallback(function() {
      if ('tocbot' in window) {
        tocbot.refresh();
        var toc = jQuery('#toc');
        if (toc.length === 0 || !tocbot) {
          return;
        }
        if (toc.find('.toc-list-item').length > 0) {
          toc.css('visibility', 'visible');
        }
      }
    });
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.11/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));

    Fluid.events.registerRefreshCallback(function() {
      if ('anchors' in window) {
        anchors.removeAll();
        var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
        var res = [];
        for (var item of el) {
          res.push('.markdown-body > ' + item.trim());
        }
        if (CONFIG.anchorjs.placement === 'left') {
          anchors.options.class = 'anchorjs-link-left';
        }
        anchors.add(res.join(', '));
      }
    });
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script  src="/blog/js/local-search.js" ></script>

  <script defer src="/blog/js/leancloud.js" ></script>




    
<script src="https://cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>



<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/blog/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
